diff options
author | Jose E. Marchesi <jose.marchesi@oracle.com> | 2023-07-30 21:01:03 +0200 |
---|---|---|
committer | Jose E. Marchesi <jose.marchesi@oracle.com> | 2023-07-30 21:08:14 +0200 |
commit | 0346042938539324a5052cc09023c1fe426e8b31 (patch) | |
tree | a23d300951c394601cb51ceeef7719a8788b962c /gas/config | |
parent | 82be476a08a2d6ee787af9a463386fab5dd85fdd (diff) | |
download | binutils-0346042938539324a5052cc09023c1fe426e8b31.zip binutils-0346042938539324a5052cc09023c1fe426e8b31.tar.gz binutils-0346042938539324a5052cc09023c1fe426e8b31.tar.bz2 |
bpf: gas: add field overflow checking to the BPF assembler
This patch makes the BPF assembler to throughfully check for overflow
in immediates. This includes relaxed instructions.
Tested in bpf-unknown-none.
gas/ChangeLog:
2023-07-30 Jose E. Marchesi <jose.marchesi@oracle.com>
* config/tc-bpf.c (signed_overflow): Copy function from
tc-aarch64.c.
(encode_insn): Check for overflow in constant immediates.
(add_relaxed_insn): Pass relax argument to encode_insn.
(add_fixed_insn): Likewise.
* testsuite/gas/bpf/disp16-overflow.d: New file.
* testsuite/gas/bpf/disp16-overflow.s: Likewise.
* testsuite/gas/bpf/disp16-overflow.l: Likewise.
* testsuite/gas/bpf/disp32-overflow.d: Likewise.
* testsuite/gas/bpf/disp32-overflow.s: Likewise.
* testsuite/gas/bpf/disp32-overflow.l: Likewise.
* testsuite/gas/bpf/imm32-overflow.d: Likewise.
* testsuite/gas/bpf/imm32-overflow.s: Likewise.
* testsuite/gas/bpf/imm32-overflow.l: Likewise.
* testsuite/gas/bpf/offset16-overflow.d: Likewise.
* testsuite/gas/bpf/offset16-overflow.s: Likewise.
* testsuite/gas/bpf/offset16-overflow.l: Likewise.
* testsuite/gas/bpf/disp16-overflow-relax.d: Likewise.
* testsuite/gas/bpf/disp16-overflow-relax.l: Likewise.
* testsuite/gas/bpf/disp16-overflow-relax.s: Likewise.
* testsuite/gas/bpf/jump-relax-jump-be.d: New file.
* testsuite/gas/bpf/bpf.exp: Run new tests.
Diffstat (limited to 'gas/config')
-rw-r--r-- | gas/config/tc-bpf.c | 63 |
1 files changed, 55 insertions, 8 deletions
diff --git a/gas/config/tc-bpf.c b/gas/config/tc-bpf.c index 230e499..7e1bd5d 100644 --- a/gas/config/tc-bpf.c +++ b/gas/config/tc-bpf.c @@ -272,6 +272,20 @@ md_section_align (segT segment, valueT size) return ((size + (1 << align) - 1) & -(1 << align)); } +/* Return non-zero if the indicated VALUE has overflowed the maximum + range expressible by an signed number with the indicated number of + BITS. */ + +static bool +signed_overflow (offsetT value, unsigned bits) +{ + offsetT lim; + if (bits >= sizeof (offsetT) * 8) + return false; + lim = (offsetT) 1 << (bits - 1); + return (value < -lim || value >= lim); +} + /* Functions concerning relocs. */ @@ -549,6 +563,11 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, disp_is_known = 1; } + /* The displacement should fit in a signed 32-bit number. */ + if (disp_is_known && signed_overflow (disp_to_target, 32)) + as_bad_where (fragp->fr_file, fragp->fr_line, + _("signed instruction operand out of range, shall fit in 32 bits")); + /* Now relax particular jump instructions. */ if (code == BPF_CODE_JA) { @@ -835,7 +854,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) immediates are encoded as zeroes. */ static void -encode_insn (struct bpf_insn *insn, char *bytes) +encode_insn (struct bpf_insn *insn, char *bytes, int relaxed) { uint8_t src, dst; @@ -889,16 +908,44 @@ encode_insn (struct bpf_insn *insn, char *bytes) /* Now the immediates that are known to be constant. */ if (insn->has_imm32 && insn->imm32.X_op == O_constant) - encode_int32 (insn->imm32.X_add_number, bytes + 4); + { + int64_t imm = insn->imm32.X_add_number; + + if (signed_overflow (imm, 32)) + as_bad (_("signed immediate out of range, shall fit in 32 bits")); + else + encode_int32 (insn->imm32.X_add_number, bytes + 4); + } if (insn->has_disp32 && insn->disp32.X_op == O_constant) - encode_int32 (insn->disp32.X_add_number, bytes + 4); + { + int64_t disp = insn->disp32.X_add_number; + + if (signed_overflow (disp, 32)) + as_bad (_("signed pc-relative offset out of range, shall fit in 32 bits")); + else + encode_int32 (insn->disp32.X_add_number, bytes + 4); + } if (insn->has_offset16 && insn->offset16.X_op == O_constant) - encode_int16 (insn->offset16.X_add_number, bytes + 2); + { + int64_t offset = insn->offset16.X_add_number; + + if (signed_overflow (offset, 16)) + as_bad (_("signed pc-relative offset out of range, shall fit in 16 bits")); + else + encode_int16 (insn->offset16.X_add_number, bytes + 2); + } if (insn->has_disp16 && insn->disp16.X_op == O_constant) - encode_int16 (insn->disp16.X_add_number, bytes + 2); + { + int64_t disp = insn->disp16.X_add_number; + + if (!relaxed && signed_overflow (disp, 16)) + as_bad (_("signed pc-relative offset out of range, shall fit in 16 bits")); + else + encode_int16 (insn->disp16.X_add_number, bytes + 2); + } if (insn->has_imm64 && insn->imm64.X_op == O_constant) { @@ -1105,7 +1152,7 @@ add_fixed_insn (struct bpf_insn *insn) /* First encode the known parts of the instruction, including opcodes and constant immediates, and write them to the frag. */ - encode_insn (insn, bytes); + encode_insn (insn, bytes, 0 /* relax */); for (i = 0; i < insn->size; ++i) md_number_to_chars (this_frag + i, (valueT) bytes[i], 1); @@ -1136,7 +1183,7 @@ add_relaxed_insn (struct bpf_insn *insn, expressionS *exp) /* First encode the known parts of the instruction, including opcodes and constant immediates, and write them to the frag. */ - encode_insn (insn, bytes); + encode_insn (insn, bytes, 1 /* relax */); for (i = 0; i < insn->size; ++i) md_number_to_chars (this_frag + i, (valueT) bytes[i], 1); @@ -1555,7 +1602,7 @@ md_assemble (char *str ATTRIBUTE_UNUSED) #undef PARSE_ERROR /* Generate the frags and fixups for the parsed instruction. */ - if (do_relax && insn.is_relaxable) + if (do_relax && isa_spec >= BPF_V4 && insn.is_relaxable) { expressionS *relaxable_exp = NULL; |