pe/coff - add support for base64 encoded long section names

PR 30444
  * coffcode.h (coff_write_object_contents): Handle base64 encoding on PE.  Also check for too large string table.
  * coffgen.c (extract_long_section_name): New function extracted from ... (make_a_section_from_file): ... here.  Add support for base64 long section names. (decode_base64): New function.
This commit is contained in:
Tristan Gingold
2023-05-31 11:20:55 +01:00
committed by Nick Clifton
parent a15891aaea
commit 768d1d879b
3 changed files with 152 additions and 31 deletions

View File

@@ -1,3 +1,14 @@
2023-05-31 Tristan Gingold <tgingold@free.fr>
PR 30444
* coffcode.h (coff_write_object_contents): Handle base64 encoding
on PE. Also check for too large string table.
* coffgen.c (extract_long_section_name): New function extracted
from ...
(make_a_section_from_file): ... here. Add support for base64
long section names.
(decode_base64): New function.
2023-05-17 Luca Bacci <luca.bacci@outlook.com>
PR 30421

View File

@@ -3568,18 +3568,55 @@ coff_write_object_contents (bfd * abfd)
len = strlen (current->name);
if (len > SCNNMLEN)
{
/* The s_name field is defined to be NUL-padded but need not be
NUL-terminated. We use a temporary buffer so that we can still
sprintf all eight chars without splatting a terminating NUL
over the first byte of the following member (s_paddr). */
/* PR 21096: The +20 is to stop a bogus warning from gcc7 about
a possible buffer overflow. */
char s_name_buf[SCNNMLEN + 1 + 20];
/* An inherent limitation of the /nnnnnnn notation used to indicate
the offset of the long name in the string table is that we
cannot address entries beyone the ten million byte boundary. */
if (string_size >= 10000000)
if (string_size < 10000000)
{
/* The s_name field is defined to be NUL-padded but need not
be NUL-terminated. We use a temporary buffer so that we
can still sprintf all eight chars without splatting a
terminating NUL over the first byte of the following
member (s_paddr). */
/* PR 21096: The +20 is to stop a bogus warning from gcc7
about a possible buffer overflow. */
char s_name_buf[SCNNMLEN + 1 + 20];
/* We do not need to use snprintf here as we have already
verified that string_size is not too big, plus we have
an overlarge buffer, just in case. */
sprintf (s_name_buf, "/%lu", (unsigned long) string_size);
/* Then strncpy takes care of any padding for us. */
strncpy (section.s_name, s_name_buf, SCNNMLEN);
}
else
#ifdef COFF_WITH_PE
{
/* PE use a base 64 encoding for long section names whose
index is very large. But contrary to RFC 4648, there is
no padding: 6 characters must be generated. */
static const char base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
unsigned long off = string_size;
unsigned i;
section.s_name[0] = '/';
section.s_name[1] = '/';
for (i = SCNNMLEN - 1; i >= 2; i--)
{
section.s_name[i] = base64[off & 0x3f];
off >>= 6;
}
}
#endif
if (string_size > 0xffffffffUL - (len + 1)
#ifndef COFF_WITH_PE
|| string_size >= 10000000
#endif
)
{
bfd_set_error (bfd_error_file_too_big);
_bfd_error_handler
@@ -3589,12 +3626,6 @@ coff_write_object_contents (bfd * abfd)
return false;
}
/* We do not need to use snprintf here as we have already verfied
that string_size is not too big, plus we have an overlarge
buffer, just in case. */
sprintf (s_name_buf, "/%lu", (unsigned long) string_size);
/* Then strncpy takes care of any padding for us. */
strncpy (section.s_name, s_name_buf, SCNNMLEN);
string_size += len + 1;
long_section_names = true;
}

View File

@@ -44,6 +44,69 @@
#include "libcoff.h"
#include "hashtab.h"
/* Extract a long section name at STRINDEX and copy it to the bfd objstack.
Return NULL in case of error. */
static char *
extract_long_section_name(bfd *abfd, unsigned long strindex)
{
const char *strings;
char *name;
strings = _bfd_coff_read_string_table (abfd);
if (strings == NULL)
return NULL;
if ((bfd_size_type)(strindex + 2) >= obj_coff_strings_len (abfd))
return NULL;
strings += strindex;
name = (char *) bfd_alloc (abfd, (bfd_size_type) strlen (strings) + 1);
if (name == NULL)
return NULL;
strcpy (name, strings);
return name;
}
/* Decode a base 64 coded string at STR of length LEN, and write the result
to RES. Return true on success.
Return false in case of invalid character or overflow. */
static bool
decode_base64 (const char *str, unsigned len, uint32_t *res)
{
unsigned i;
uint32_t val;
val = 0;
for (i = 0; i < len; i++)
{
char c = str[i];
unsigned d;
if (c >= 'A' && c <= 'Z')
d = c - 'A';
else if (c >= 'a' && c <= 'z')
d = c - 'a' + 26;
else if (c >= '0' && c <= '9')
d = c - '0' + 52;
else if (c == '+')
d = 62;
else if (c == '/')
d = 63;
else
return false;
/* Check for overflow. */
if ((val >> 26) != 0)
return false;
val = (val << 6) + d;
}
*res = val;
return true;
}
/* Take a section header read from a coff file (in HOST byte order),
and make a BFD "section" out of it. This is used by ECOFF. */
@@ -68,32 +131,48 @@ make_a_section_from_file (bfd *abfd,
if (bfd_coff_set_long_section_names (abfd, bfd_coff_long_section_names (abfd))
&& hdr->s_name[0] == '/')
{
char buf[SCNNMLEN];
long strindex;
char *p;
const char *strings;
/* Flag that this BFD uses long names, even though the format might
expect them to be off by default. This won't directly affect the
format of any output BFD created from this one, but the information
can be used to decide what to do. */
bfd_coff_set_long_section_names (abfd, true);
memcpy (buf, hdr->s_name + 1, SCNNMLEN - 1);
buf[SCNNMLEN - 1] = '\0';
strindex = strtol (buf, &p, 10);
if (*p == '\0' && strindex >= 0)
if (hdr->s_name[1] == '/')
{
strings = _bfd_coff_read_string_table (abfd);
if (strings == NULL)
/* LLVM extension: the '/' is followed by another '/' and then by
the index in the strtab encoded in base64 without NUL at the
end. */
uint32_t strindex;
/* Decode the index. No overflow is expected as the string table
length is at most 2^32 - 1 (the length is written on the first
four bytes).
Also, contrary to RFC 4648, all the characters must be decoded,
there is no padding. */
if (!decode_base64 (hdr->s_name + 2, SCNNMLEN - 2, &strindex))
return false;
if ((bfd_size_type)(strindex + 2) >= obj_coff_strings_len (abfd))
return false;
strings += strindex;
name = (char *) bfd_alloc (abfd,
(bfd_size_type) strlen (strings) + 1 + 1);
name = extract_long_section_name (abfd, strindex);
if (name == NULL)
return false;
strcpy (name, strings);
}
else
{
/* PE classic long section name. The '/' is followed by the index
in the strtab. The index is formatted as a decimal string. */
char buf[SCNNMLEN];
long strindex;
char *p;
memcpy (buf, hdr->s_name + 1, SCNNMLEN - 1);
buf[SCNNMLEN - 1] = '\0';
strindex = strtol (buf, &p, 10);
if (*p == '\0' && strindex >= 0)
{
name = extract_long_section_name (abfd, strindex);
if (name == NULL)
return false;
}
}
}