>From b592d9041eb083e23cee462dbc27b8cc25b52917 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 19 May 2017 15:39:06 -0700 Subject: [PATCH 2/2] argp: fix shift bug * lib/argp-parse.c (parser_parse_opt): Rework to avoid undefined behavior on shift overflow, caught by gcc -fsanitize=undefined. --- ChangeLog | 4 ++++ lib/argp-parse.c | 22 ++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index b214252..46714cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2017-05-19 Paul Eggert + 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. diff --git a/lib/argp-parse.c b/lib/argp-parse.c index 3f723bc..4723a2b 100644 --- a/lib/argp-parse.c +++ b/lib/argp-parse.c @@ -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 -- 2.9.4