From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp4-g21.free.fr (smtp4-g21.free.fr [IPv6:2a01:e0c:1:1599::13]) by sourceware.org (Postfix) with ESMTPS id 1DC22385770D for ; Wed, 24 May 2023 17:33:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1DC22385770D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=free.fr Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=free.fr Received: from [192.168.2.42] (unknown [88.173.63.180]) (Authenticated sender: tgingold@free.fr) by smtp4-g21.free.fr (Postfix) with ESMTPSA id EFF3F19F593; Wed, 24 May 2023 19:33:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=free.fr; s=smtp-20201208; t=1684949632; bh=es8O/Vnl4ymE+whmalltN4HgIttPjVjICdGltByBkB0=; h=Date:Subject:To:Cc:References:From:In-Reply-To:From; b=pK/4BPKkqwlsDEfHwdhKdUaoYh2Jeq1GW0TXdkVJuj7kqCHRhVqR551clZz7yhFrX IrvWHJ801rLi5Tyg+u/ZVfYqONEZPNSy7JFMu7olUEUE4KXiEaEXpBPKIVzsGTD2NO ZPISJQxIFmxSzCBFkXNI1orAXqkLNBVIHGZmPuYdJxfxYasMfIb8I2mEV8i1Uy1XEC fKse6qbBfPSsVDo+OYn0QWRS0spBxaFr+MACIa98MAOrILjXPCNcera3n8fsDUbM2/ jqsJ4ou5QijQR+XxDvKfAOO6DSSXF0fGOIoicmiMxPTukjUo9x7V1+E3T7kDY29fuy 5/svFrSDczkaA== Message-ID: Date: Wed, 24 May 2023 19:33:49 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: Re: [PATCH v2] pe/coff - add support for base64 encoded long section names To: Jan Beulich , Tristan Gingold via Binutils Cc: "Jose E. Marchesi" References: <871qj7wgn5.fsf@oracle.com> <16e8afe2-80cd-b87e-35f5-8eb8daa8239a@free.fr> Content-Language: en-US From: Tristan Gingold In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,GIT_PATCH_0,JMQ_SPF_NEUTRAL,NICE_REPLY_A,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hello, On 24/05/2023 08:29, Jan Beulich wrote: > On 24.05.2023 07:48, Tristan Gingold via Binutils wrote: >> I have adjusted the comments to clarify. Is it OK ? > > With the earlier nit addressed (I'll repeat it below), yes. But please > allow a little bit of time for in particular Nick to raise last minute > comments, as he had looked at the earlier version of the patch. Plus ... Oops, sorry. I missed your note about the nit. Now fixed. And thank you for the reminder. I have also fixed the email address in the CL. Tristan. 2023-05-24 Tristan Gingold 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. diff --git a/bfd/coffcode.h b/bfd/coffcode.h index 974c8ad9854..780b669fe9d 100644 --- a/bfd/coffcode.h +++ b/bfd/coffcode.h @@ -3625,18 +3625,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 @@ -3646,12 +3683,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; } diff --git a/bfd/coffgen.c b/bfd/coffgen.c index ac936def566..d5cef273837 100644 --- a/bfd/coffgen.c +++ b/bfd/coffgen.c @@ -43,6 +43,69 @@ #include "coff/internal.h" #include "libcoff.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. */ @@ -67,32 +130,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) - return false; - if ((bfd_size_type)(strindex + 2) >= obj_coff_strings_len (abfd)) + /* 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; - 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; + } } }