bug#78880: od Heap-buffer overflow

Paul Eggert <[email protected]>
Newsgroups gmane.comp.gnu.core-utils.bugs
Organization UCLA Computer Science Department
Message-ID <[email protected]>
On 2025-06-29 05:17, Pádraig Brady wrote:
> If we want the compiler to just apply Dead Code Elimination here,
> then it may be best to push/pop ignoring that warning ?

Or, since GCC didn't complain about similar code elsewhere, we can 
change the complained-about code to look more like the code elsewhere. 
This is more likely to pacify any non-GCC compilers that are also flummoxed.

There's a similar situation with the floating point formats, which I 
assume you didn't run into because they're all distinct on your platform.

I installed the attached patch which I hope pacifies GCC on your 
platform. I didn't observe the problem with gcc (GCC) 15.1.1 20250521 
(Red Hat 15.1.1-2) on x86-64 so I can't easily test the patch myself.
0001-od-pacify-gcc-Wduplicated-cond.patch (text/x-patch, 5.9 KB)
From dcdb2550c4a6480394fa3b6303a81c7f94461865 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Sun, 29 Jun 2025 17:12:07 -0700
Subject: [PATCH] od: pacify gcc -Wduplicated-cond
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Pádraig Brady <https://bugs.gnu.org/78880#43>.
This patch doesn’t fix any bugs; it merely pacifies GCC.
* src/od.c (ispec_to_format): New function, replacing
the old ISPEC_TO_FORMAT macro.  All uses changed.
This part of the change is just refactoring.
(decode_one_format): Pacify à la ispec_to_format.
---
 src/od.c | 56 +++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 35 insertions(+), 21 deletions(-)

diff --git a/src/od.c b/src/od.c
index 50319aa83..3381ee484 100644
--- a/src/od.c
+++ b/src/od.c
@@ -668,6 +668,21 @@ simple_strtoi (char const *s, char const **p, int *val)
   return true;
 }
 
+/* Return a printf format for SPEC, given that remaining arguments are
+   formats for int, long, long long, and intmax_t, respectively.  */
+static char const *
+ispec_to_format (enum size_spec size_spec,
+                 char const *int_format,
+                 char const *long_format,
+                 char const *long_long_format,
+                 char const *intmax_format)
+{
+  return (LONG < INTMAX && size_spec == INTMAX ? intmax_format
+          : LONG < LONG_LONG && size_spec == LONG_LONG ? long_long_format
+          : INT < LONG && size_spec == LONG ? long_format
+          : int_format);
+}
+
 /* If S points to a single valid modern od format string, put
    a description of that format in *TSPEC, make *NEXT point at the
    character following the just-decoded format (if *NEXT is non-null),
@@ -756,12 +771,6 @@ decode_one_format (char const *s_orig, char const *s, char const **next,
           break;
         }
 
-#define ISPEC_TO_FORMAT(Spec, Min_fmt, Long_fmt, Long_long_fmt, Max_fmt) \
-  (LONG < INTMAX && (Spec) == INTMAX ? (Max_fmt)			\
-   : LONG < LONG_LONG && (Spec) == LONG_LONG ? (Long_long_fmt)		\
-   : INT < LONG && (Spec) == LONG ? (Long_fmt)				\
-   : (Min_fmt))
-
       size_spec = integral_type_size[size];
 
       switch (c)
@@ -770,28 +779,28 @@ decode_one_format (char const *s_orig, char const *s, char const **next,
           fmt = SIGNED_DECIMAL;
           field_width = INT_BITS_STRLEN_BOUND (CHAR_BIT * size - 1) + 1;
           sprintf (tspec->fmt_string, "%%*%s",
-                   ISPEC_TO_FORMAT (size_spec, "d", "ld", "lld", "jd"));
+                   ispec_to_format (size_spec, "d", "ld", "lld", "jd"));
           break;
 
         case 'o':
           fmt = OCTAL;
           field_width = (CHAR_BIT * size + 2) / 3;
           sprintf (tspec->fmt_string, "%%*.%d%s", field_width,
-                   ISPEC_TO_FORMAT (size_spec, "o", "lo", "llo", "jo"));
+                   ispec_to_format (size_spec, "o", "lo", "llo", "jo"));
           break;
 
         case 'u':
           fmt = UNSIGNED_DECIMAL;
           field_width = INT_BITS_STRLEN_BOUND (CHAR_BIT * size);
           sprintf (tspec->fmt_string, "%%*%s",
-                   ISPEC_TO_FORMAT (size_spec, "u", "lu", "llu", "ju"));
+                   ispec_to_format (size_spec, "u", "lu", "llu", "ju"));
           break;
 
         case 'x':
           fmt = HEXADECIMAL;
           field_width = (CHAR_BIT * size + 3) / 4;
           sprintf (tspec->fmt_string, "%%*.%d%s", field_width,
-                   ISPEC_TO_FORMAT (size_spec, "x", "lx", "llx", "jx"));
+                   ispec_to_format (size_spec, "x", "lx", "llx", "jx"));
           break;
 
         default:
@@ -801,14 +810,18 @@ decode_one_format (char const *s_orig, char const *s, char const **next,
       /* Prefer INT, prefer LONG to longer types,
          and prefer INTMAX to LONG_LONG.  */
       print_function
-        = (size_spec == INT ? print_int
-           : size_spec == SHORT ? (fmt == SIGNED_DECIMAL
-                                   ? print_s_short : print_short)
-           : size_spec == CHAR ? (fmt == SIGNED_DECIMAL
-                                  ? print_s_char : print_char)
-           : size_spec == LONG ? print_long
-           : size_spec == INTMAX ? print_intmax
-           : size_spec == LONG_LONG ? print_long_long
+        = (size_spec == INT
+           ? print_int
+           : SHORT < INT && size_spec == SHORT
+           ? (fmt == SIGNED_DECIMAL ? print_s_short : print_short)
+           : CHAR < SHORT && size_spec == CHAR
+           ? (fmt == SIGNED_DECIMAL ? print_s_char : print_char)
+           : INT < LONG && size_spec == LONG
+           ? print_long
+           : LONG < INTMAX && size_spec == INTMAX
+           ? print_intmax
+           : LONG < LONG_LONG && LONG_LONG < INTMAX && size_spec == LONG_LONG
+           ? print_long_long
            : (affirm (false), nullptr));
       break;
 
@@ -892,18 +905,19 @@ decode_one_format (char const *s_orig, char const *s, char const **next,
             print_function = print_double;
             field_width = DBL_STRLEN_BOUND_L (decimal_point_len);
           }
-        else if (size_spec == FLOAT_SINGLE)
+        else if (FLOAT_SINGLE < FLOAT_DOUBLE && size_spec == FLOAT_SINGLE)
           {
             print_function = print_float;
             field_width = FLT_STRLEN_BOUND_L (decimal_point_len);
           }
-        else if (size_spec == FLOAT_HALF)
+        else if (FLOAT_HALF < FLOAT_SINGLE && size_spec == FLOAT_HALF)
           {
             print_function = fmt == BFLOATING_POINT
                              ? print_bfloat : print_halffloat;
             field_width = FLT_STRLEN_BOUND_L (decimal_point_len);
           }
-        else if (size_spec == FLOAT_LONG_DOUBLE)
+        else if (FLOAT_DOUBLE < FLOAT_LONG_DOUBLE
+                 && size_spec == FLOAT_LONG_DOUBLE)
           {
             print_function = print_long_double;
             field_width = LDBL_STRLEN_BOUND_L (decimal_point_len);
-- 
2.48.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.