]> Savannah Git Hosting - gnulib.git/commitdiff
argp: fix shift bug
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 19 May 2017 22:39:06 +0000 (15:39 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 19 May 2017 22:39:44 +0000 (15:39 -0700)
* lib/argp-parse.c (parser_parse_opt): Rework to avoid undefined
behavior on shift overflow, caught by gcc -fsanitize=undefined.

ChangeLog
lib/argp-parse.c

index b214252007835815dcf57189972f569497df0c78..46714cdd49d034b62b1dcacc0cd16921dad6829e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2017-05-19  Paul Eggert  <eggert@cs.ucla.edu>
 
+       argp: fix shift bug
+       * lib/argp-parse.c (parser_parse_opt): Rework to avoid undefined
+       behavior on shift overflow, caught by gcc -fsanitize=undefined.
+
        argp: fix pointer-subtraction bug
        * lib/argp-help.c (hol_append): Don’t subtract pointers to
        different arrays, as this can run afoul of -fcheck-pointer-bounds.
index 3f723bc83fe0efa7bb9a60de1472657c5523ee6c..4723a2b29dda9e4eebec549a882018b064565c90 100644 (file)
@@ -740,12 +740,22 @@ parser_parse_opt (struct parser *parser, int opt, char *val)
             }
     }
   else
-    /* A long option.  We use shifts instead of masking for extracting
-       the user value in order to preserve the sign.  */
-    err =
-      group_parse (&parser->groups[group_key - 1], &parser->state,
-                   (opt << GROUP_BITS) >> GROUP_BITS,
-                   parser->opt_data.optarg);
+    /* A long option.  Preserve the sign in the user key, without
+       invoking undefined behavior.  Assume two's complement.  */
+    {
+      unsigned uopt = opt;
+      unsigned ushifted_user_key = uopt << GROUP_BITS;
+      int shifted_user_key = ushifted_user_key;
+      int user_key;
+      if (-1 >> 1 == -1)
+        user_key = shifted_user_key >> GROUP_BITS;
+      else
+        user_key = ((ushifted_user_key >> GROUP_BITS)
+                    - (shifted_user_key < 0 ? 1 << USER_BITS : 0));
+      err =
+        group_parse (&parser->groups[group_key - 1], &parser->state,
+                     user_key, parser->opt_data.optarg);
+    }
 
   if (err == EBADKEY)
     /* At least currently, an option not recognized is an error in the