diff mbox series

[v2,03/11] x86/mkreloc: use the string table to get names

Message ID 20250401130840.72119-4-roger.pau@citrix.com (mailing list archive)
State New
Headers show
Series x86/EFI: prevent write-execute sections | expand

Commit Message

Roger Pau Monne April 1, 2025, 1:08 p.m. UTC
When using PE format names greater than 8 characters are placed in the
string table, and a reference using the '/<offset>' format is placed in the
name field.  Read the string table if present, and decode names as
required.

No functional change intended, but the name references printed in error
messages are now human readable:

Warning: relocation to r/o section /4:00042d43

Becomes:

Warning: relocation to r/o section .init.text:000446c3

Note the introduced helper to print names relies on a static internal
buffer to make sure the returned string are always null terminated.
This is enough for the current use-case, but if the returned value is to
stay valid between calls the current static buffer won't work as expected.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
 xen/arch/x86/efi/mkreloc.c | 69 +++++++++++++++++++++++++++++++++-----
 1 file changed, 61 insertions(+), 8 deletions(-)

Comments

Jan Beulich April 1, 2025, 3:50 p.m. UTC | #1
On 01.04.2025 15:08, Roger Pau Monne wrote:
> --- a/xen/arch/x86/efi/mkreloc.c
> +++ b/xen/arch/x86/efi/mkreloc.c
> @@ -17,6 +17,12 @@
>  #define PE_BASE_RELOC_HIGHLOW  3
>  #define PE_BASE_RELOC_DIR64   10
>  
> +/* The size of a symbol table entry is always 18 bytes. */
> +#define SYM_SIZE 18
> +
> +const char *string_table;
> +unsigned int string_table_size;

The way you use it, imo this wants to be uint32_t.

Both probably want to be static.

> @@ -25,6 +31,28 @@ static void usage(const char *cmd, int rc)
>      exit(rc);
>  }
>  
> +const char *get_name(const char *name)
> +{
> +    static char buffer[sizeof(((struct section_header *)NULL)->name) + 1] = {};

As this makes things section specific, may I suggest to name the function
e.g. get_section_name()? Also better to be static again.

> +    unsigned long offset;
> +
> +    if ( name[0] != '/' )
> +    {
> +        /*
> +         * Use a temporary buffer in case the name is 8 characters long, as
> +         * then there's no terminating null character in the input string.
> +         */
> +        strncpy(buffer, name, sizeof(buffer) - 1);
> +        return buffer;
> +    }
> +
> +    offset = strtoul(&name[1], NULL, 10);

Don't you need to nul-terminate the string here, too, to play safe? (Yes,
we don't expect this big a string table.)

> +    if ( !string_table || offset < 4 || offset >= string_table_size )

Considering how you reduce string_table_size after having read it from
the image, don't you mean "offset - 4 >= string_table_size" here? Also
below you use sizeof(string_table_size) instead of a literal 4.

> +        return name;
> +
> +    return &string_table[offset - 4];

Here as well.

> @@ -83,6 +111,31 @@ static unsigned int load(const char *name, int *handle,
>          exit(3);
>      }
>  
> +    if ( !string_table && pe_hdr.symbol_table )
> +    {
> +        char *strings;
> +
> +        if ( lseek(in, pe_hdr.symbol_table + pe_hdr.symbols * SYM_SIZE,
> +                   SEEK_SET) < 0 ||
> +             read(in, &string_table_size, sizeof(string_table_size)) !=
> +             sizeof(string_table_size) )
> +        {
> +            perror(name);
> +            exit(3);
> +        }
> +
> +        string_table_size -= sizeof(string_table_size);

You're careful of underflow above; better be careful here, too?

> +        strings = malloc(string_table_size);

You check for all other errors, just not here?

Jan
Jan Beulich April 2, 2025, 7:42 a.m. UTC | #2
On 01.04.2025 15:08, Roger Pau Monne wrote:
> @@ -83,6 +111,31 @@ static unsigned int load(const char *name, int *handle,
>          exit(3);
>      }
>  
> +    if ( !string_table && pe_hdr.symbol_table )
> +    {
> +        char *strings;
> +
> +        if ( lseek(in, pe_hdr.symbol_table + pe_hdr.symbols * SYM_SIZE,
> +                   SEEK_SET) < 0 ||
> +             read(in, &string_table_size, sizeof(string_table_size)) !=
> +             sizeof(string_table_size) )
> +        {
> +            perror(name);
> +            exit(3);
> +        }
> +
> +        string_table_size -= sizeof(string_table_size);
> +        strings = malloc(string_table_size);

One more thing: Perhaps better to allocate an extra byte here, ...

> +        if ( read(in, strings, string_table_size) != string_table_size )
> +        {
> +            perror(name);
> +            exit(3);
> +        }
> +
> +        string_table = strings;
> +    }

... and then put a nul terminator at the end, just in case.

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/efi/mkreloc.c b/xen/arch/x86/efi/mkreloc.c
index 1a6cfc845cba..cc106bd875ba 100644
--- a/xen/arch/x86/efi/mkreloc.c
+++ b/xen/arch/x86/efi/mkreloc.c
@@ -17,6 +17,12 @@ 
 #define PE_BASE_RELOC_HIGHLOW  3
 #define PE_BASE_RELOC_DIR64   10
 
+/* The size of a symbol table entry is always 18 bytes. */
+#define SYM_SIZE 18
+
+const char *string_table;
+unsigned int string_table_size;
+
 static void usage(const char *cmd, int rc)
 {
     fprintf(rc ? stderr : stdout,
@@ -25,6 +31,28 @@  static void usage(const char *cmd, int rc)
     exit(rc);
 }
 
+const char *get_name(const char *name)
+{
+    static char buffer[sizeof(((struct section_header *)NULL)->name) + 1] = {};
+    unsigned long offset;
+
+    if ( name[0] != '/' )
+    {
+        /*
+         * Use a temporary buffer in case the name is 8 characters long, as
+         * then there's no terminating null character in the input string.
+         */
+        strncpy(buffer, name, sizeof(buffer) - 1);
+        return buffer;
+    }
+
+    offset = strtoul(&name[1], NULL, 10);
+    if ( !string_table || offset < 4 || offset >= string_table_size )
+        return name;
+
+    return &string_table[offset - 4];
+}
+
 static unsigned int load(const char *name, int *handle,
                          struct section_header **sections,
                          uint_fast64_t *image_base,
@@ -83,6 +111,31 @@  static unsigned int load(const char *name, int *handle,
         exit(3);
     }
 
+    if ( !string_table && pe_hdr.symbol_table )
+    {
+        char *strings;
+
+        if ( lseek(in, pe_hdr.symbol_table + pe_hdr.symbols * SYM_SIZE,
+                   SEEK_SET) < 0 ||
+             read(in, &string_table_size, sizeof(string_table_size)) !=
+             sizeof(string_table_size) )
+        {
+            perror(name);
+            exit(3);
+        }
+
+        string_table_size -= sizeof(string_table_size);
+        strings = malloc(string_table_size);
+
+        if ( read(in, strings, string_table_size) != string_table_size )
+        {
+            perror(name);
+            exit(3);
+        }
+
+        string_table = strings;
+    }
+
     *sections = malloc(pe_hdr.sections * sizeof(**sections));
     if ( !*sections )
     {
@@ -173,8 +226,8 @@  static void diff_sections(const unsigned char *ptr1, const unsigned char *ptr2,
         if ( i < disp || i + width - disp > sec->raw_data_size )
         {
             fprintf(stderr,
-                    "Bogus difference at %.8s:%08" PRIxFAST32 "\n",
-                    sec->name, i);
+                    "Bogus difference at %s:%08" PRIxFAST32 "\n",
+                    get_name(sec->name), i);
             exit(3);
         }
 
@@ -184,9 +237,9 @@  static void diff_sections(const unsigned char *ptr1, const unsigned char *ptr2,
         if ( delta != diff )
         {
             fprintf(stderr,
-                    "Difference at %.8s:%08" PRIxFAST32 " is %#" PRIxFAST64
+                    "Difference at %s:%08" PRIxFAST32 " is %#" PRIxFAST64
                     " (expected %#" PRIxFAST64 ")\n",
-                    sec->name, i - disp, delta, diff);
+                    get_name(sec->name), i - disp, delta, diff);
             continue;
         }
         if ( width == 8 && (val1.u64 < base || val1.u64 > end) )
@@ -210,15 +263,15 @@  static void diff_sections(const unsigned char *ptr1, const unsigned char *ptr2,
         else if ( rva != cur_rva )
         {
             fprintf(stderr,
-                    "Cannot handle decreasing RVA (at %.8s:%08" PRIxFAST32 ")\n",
-                    sec->name, i - disp);
+                    "Cannot handle decreasing RVA (at %s:%08" PRIxFAST32 ")\n",
+                    get_name(sec->name), i - disp);
             exit(3);
         }
 
         if ( !(sec->flags & IMAGE_SCN_MEM_WRITE) )
             fprintf(stderr,
-                    "Warning: relocation to r/o section %.8s:%08" PRIxFAST32 "\n",
-                    sec->name, i - disp);
+                    "Warning: relocation to r/o section %s:%08" PRIxFAST32 "\n",
+                    get_name(sec->name), i - disp);
 
         printf("\t.word (%u << 12) | 0x%03" PRIxFAST32 "\n",
                reloc, sec->rva + i - disp - rva);