From: Alan Modra Date: Wed, 26 Aug 2020 08:09:58 +0000 (+0930) Subject: PR26448 UBSAN: symbols.c:1586 left shift of negative value X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d8d6da137d7ececcd0e10c575aa187bb8c9b24e0;p=binutils-gdb.git PR26448 UBSAN: symbols.c:1586 left shift of negative value Besides avoiding the UB, this also makes right shifts inside expression symbols unsigned, consistent with the way gas evaluates expressions in source. PR 26448 * symbols.c: Include limits.h. (resolve_symbol_value ): Do an unsigned shift. Warn if shift count larger than valueT size. --- diff --git a/gas/ChangeLog b/gas/ChangeLog index cfa087ed6ac..c56b992685c 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,10 @@ +2020-08-26 Alan Modra + + PR 26448 + * symbols.c: Include limits.h. + (resolve_symbol_value ): Do an + unsigned shift. Warn if shift count larger than valueT size. + 2020-08-26 Alan Modra PR 26447 diff --git a/gas/symbols.c b/gas/symbols.c index 67ac8012958..d6080886be8 100644 --- a/gas/symbols.c +++ b/gas/symbols.c @@ -26,6 +26,13 @@ #include "subsegs.h" #include "write.h" +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + struct symbol_flags { /* Whether the symbol is a local_symbol. */ @@ -1559,14 +1566,24 @@ resolve_symbol_value (symbolS *symp) right = 1; } + if ((op == O_left_shift || op == O_right_shift) + && (valueT) right >= sizeof (valueT) * CHAR_BIT) + { + as_warn_value_out_of_range (_("shift count"), right, 0, + sizeof (valueT) * CHAR_BIT - 1, + NULL, 0); + left = right = 0; + } switch (symp->x->value.X_op) { case O_multiply: left *= right; break; case O_divide: left /= right; break; case O_modulus: left %= right; break; - case O_left_shift: left <<= right; break; - case O_right_shift: left >>= right; break; + case O_left_shift: + left = (valueT) left << (valueT) right; break; + case O_right_shift: + left = (valueT) left >> (valueT) right; break; case O_bit_inclusive_or: left |= right; break; case O_bit_or_not: left |= ~right; break; case O_bit_exclusive_or: left ^= right; break;