--- a/sys/dev/drm/radeon/atom.c +++ b/sys/dev/drm/radeon/atom.c @@ -84,6 +84,20 @@ }; static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 }; +/* ATOM shift counts come from untrusted VBIOS operands (bytes or full + * dwords); counts >= 32 are undefined behaviour in C and are compiled + * inconsistently (observed: gcc -O1 folds 0xffff << 40 to 0). Define the + * intended hardware semantics: shifting by >= the width yields 0. */ +static uint32_t atom_shl(uint32_t v, uint32_t s) +{ + return s < 32 ? (v << s) : 0; +} + +static uint32_t atom_shr(uint32_t v, uint32_t s) +{ + return s < 32 ? (v >> s) : 0; +} + static int debug_depth = 0; #ifdef ATOM_DEBUG static void debug_print_spaces(int n) @@ -248,12 +262,12 @@ case ATOM_WS_SHIFT: val = gctx->shift; break; - case ATOM_WS_OR_MASK: - val = 1 << gctx->shift; - break; - case ATOM_WS_AND_MASK: - val = ~(1 << gctx->shift); - break; + case ATOM_WS_OR_MASK: + val = atom_shl(1, gctx->shift); + break; + case ATOM_WS_AND_MASK: + val = ~atom_shl(1, gctx->shift); + break; case ATOM_WS_FB_WINDOW: val = gctx->fb_base; break; @@ -901,7 +915,7 @@ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); ATOM_SDEBUG_PRINT(" shift: %d\n", shift); - dst <<= shift; + dst = atom_shl(dst, shift); ATOM_SDEBUG_PRINT(" dst: "); atom_put_dst(ctx, arg, attr, &dptr, dst, saved); } @@ -917,7 +931,7 @@ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); ATOM_SDEBUG_PRINT(" shift: %d\n", shift); - dst >>= shift; + dst = atom_shr(dst, shift); ATOM_SDEBUG_PRINT(" dst: "); atom_put_dst(ctx, arg, attr, &dptr, dst, saved); } @@ -934,7 +948,7 @@ dst = saved; shift = atom_get_src(ctx, attr, ptr); ATOM_SDEBUG_PRINT(" shift: %d\n", shift); - dst <<= shift; + dst = atom_shl(dst, shift); dst &= atom_arg_mask[dst_align]; dst >>= atom_arg_shift[dst_align]; ATOM_SDEBUG_PRINT(" dst: "); @@ -953,7 +967,7 @@ dst = saved; shift = atom_get_src(ctx, attr, ptr); ATOM_SDEBUG_PRINT(" shift: %d\n", shift); - dst >>= shift; + dst = atom_shr(dst, shift); dst &= atom_arg_mask[dst_align]; dst >>= atom_arg_shift[dst_align]; ATOM_SDEBUG_PRINT(" dst: ");