https://github.com/akkartik/mu/blob/main/linux/bootstrap/034check_operand_bounds.cc

      1 //:: Check that the different arguments of an instruction aren't too large for their bitfields.
      2
      3 void test_check_bitfield_sizes() {
      4   Hide_errors = true;
      5   run(
      6       "== code 0x1\n"
      7       "01/add 4/mod 3/rm32 1/r32\n"  // add ECX to EBX
      8   );
      9   CHECK_TRACE_CONTENTS(
     10       "error: '4/mod' too large to fit in bitfield mod\n"
     11   );
     12 }
     13
     14 :(before "End Globals")
     15 map<string, uint32_t> Operand_bound;
     16 :(before "End One-time Setup")
     17 put_new(Operand_bound, "subop", 1<<3);
     18 put_new(Operand_bound, "mod", 1<<2);
     19 put_new(Operand_bound, "rm32", 1<<3);
     20 put_new(Operand_bound, "base", 1<<3);
     21 put_new(Operand_bound, "index", 1<<3);
     22 put_new(Operand_bound, "scale", 1<<2);
     23 put_new(Operand_bound, "r32", 1<<3);
     24 put_new(Operand_bound, "disp8", 1<<8);
     25 put_new(Operand_bound, "disp16", 1<<16);
     26 // no bound needed for disp32
     27 put_new(Operand_bound, "imm8", 1<<8);
     28 // no bound needed for imm32
     29
     30 :(before "Pack Operands(segment code)")
     31 check_argument_bounds(code);
     32 if (trace_contains_errors()) return;
     33 :(code)
     34 void check_argument_bounds(const segment& code) {
     35   trace(3, "transform") << "-- check argument bounds" << end();
     36   for (int i = 0;  i < SIZE(code.lines);  ++i) {
     37     const line& inst = code.lines.at(i);
     38     for (int j = first_argument(inst);  j < SIZE(inst.words);  ++j)
     39       check_argument_bounds(inst.words.at(j));
     40     if (trace_contains_errors()) return;  // stop at the first mal-formed instruction
     41   }
     42 }
     43
     44 void check_argument_bounds(const word& w) {
     45   for (map<string, uint32_t>::iterator p = Operand_bound.begin();  p != Operand_bound.end();  ++p) {
     46     if (!has_argument_metadata(w, p->first)) continue;
     47     if (!looks_like_hex_int(w.data)) continue;  // later transforms are on their own to do their own bounds checking
     48     int32_t x = parse_int(w.data);
     49     if (x >= 0) {
     50       if (p->first == "disp8" || p->first == "disp16") {
     51         if (static_cast<uint32_t>(x) >= p->second/2)
     52           raise << "'" << w.original << "' too large to fit in signed bitfield " << p->first << '\n' << end();
     53       }
     54       else {
     55         if (static_cast<uint32_t>(x) >= p->second)
     56           raise << "'" << w.original << "' too large to fit in bitfield " << p->first << '\n' << end();
     57       }
     58     }
     59     else {
     60       // hacky? assuming bound is a power of 2
     61       if (x < -1*static_cast<int32_t>(p->second/2))
     62         raise << "'" << w.original << "' too large to fit in bitfield " << p->first << '\n' << end();
     63     }
     64   }
     65 }
     66
     67 void test_check_bitfield_sizes_for_imm8() {
     68   run(
     69       "== code 0x1\n"
     70       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0xff/imm8"  // shift EBX left
     71   );
     72   CHECK(!trace_contains_errors());
     73 }
     74
     75 void test_check_bitfield_sizes_for_imm8_error() {
     76   Hide_errors = true;
     77   run(
     78       "== code 0x1\n"
     79       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0x100/imm8"  // shift EBX left
     80   );
     81   CHECK_TRACE_CONTENTS(
     82       "error: '0x100/imm8' too large to fit in bitfield imm8\n"
     83   );
     84 }
     85
     86 void test_check_bitfield_sizes_for_negative_imm8() {
     87   run(
     88       "== code 0x1\n"
     89       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x80/imm8"  // shift EBX left
     90   );
     91   CHECK(!trace_contains_errors());
     92 }
     93
     94 void test_check_bitfield_sizes_for_negative_imm8_error() {
     95   Hide_errors = true;
     96   run(
     97       "== code 0x1\n"
     98       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x81/imm8"  // shift EBX left
     99   );
    100   CHECK_TRACE_CONTENTS(
    101       "error: '-0x81/imm8' too large to fit in bitfield imm8\n"
    102   );
    103 }
    104
    105 void test_check_bitfield_sizes_for_disp8() {
    106   // not bothering to run
    107   transform(
    108       "== code 0x1\n"
    109       "01/add 1/mod/*+disp8 3/rm32 1/r32 0x7f/disp8\n"  // add ECX to *(EBX+0x7f)
    110   );
    111   CHECK(!trace_contains_errors());
    112 }
    113
    114 void test_check_bitfield_sizes_for_disp8_error() {
    115   Hide_errors = true;
    116   run(
    117       "== code 0x1\n"
    118       "01/add 1/mod/*+disp8 3/rm32 1/r32 0x80/disp8\n"  // add ECX to *(EBX+0x80)
    119   );
    120   CHECK_TRACE_CONTENTS(
    121       "error: '0x80/disp8' too large to fit in signed bitfield disp8\n"
    122   );
    123 }
    124
    125 void test_check_bitfield_sizes_for_negative_disp8() {
    126   // not bothering to run
    127   transform(
    128       "== code 0x1\n"
    129       "01/add 1/mod/*+disp8 3/rm32 1/r32 -0x80/disp8\n"  // add ECX to *(EBX-0x80)
    130   );
    131   CHECK(!trace_contains_errors());
    132 }
    133
    134 void test_check_bitfield_sizes_for_negative_disp8_error() {
    135   Hide_errors = true;
    136   run(
    137       "== code 0x1\n"
    138       "01/add 1/mod/*+disp8 3/rm32 1/r32 -0x81/disp8\n"  // add ECX to *(EBX-0x81)
    139   );
    140   CHECK_TRACE_CONTENTS(
    141       "error: '-0x81/disp8' too large to fit in bitfield disp8\n"
    142   );
    143 }