From 0346042938539324a5052cc09023c1fe426e8b31 Mon Sep 17 00:00:00 2001 From: "Jose E. Marchesi" Date: Sun, 30 Jul 2023 21:01:03 +0200 Subject: [PATCH] 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 * 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. --- gas/ChangeLog | 25 +++++++++++ gas/config/tc-bpf.c | 63 +++++++++++++++++++++++---- gas/testsuite/gas/bpf/bpf.exp | 7 +++ gas/testsuite/gas/bpf/disp16-overflow-relax.d | 3 ++ gas/testsuite/gas/bpf/disp16-overflow-relax.l | 3 ++ gas/testsuite/gas/bpf/disp16-overflow-relax.s | 4 ++ gas/testsuite/gas/bpf/disp16-overflow.d | 3 ++ gas/testsuite/gas/bpf/disp16-overflow.l | 3 ++ gas/testsuite/gas/bpf/disp16-overflow.s | 4 ++ gas/testsuite/gas/bpf/disp32-overflow.d | 3 ++ gas/testsuite/gas/bpf/disp32-overflow.l | 3 ++ gas/testsuite/gas/bpf/disp32-overflow.s | 4 ++ gas/testsuite/gas/bpf/imm32-overflow.d | 3 ++ gas/testsuite/gas/bpf/imm32-overflow.l | 3 ++ gas/testsuite/gas/bpf/imm32-overflow.s | 4 ++ gas/testsuite/gas/bpf/jump-relax-jump-be.d | 25 +++++++++++ gas/testsuite/gas/bpf/offset16-overflow.d | 3 ++ gas/testsuite/gas/bpf/offset16-overflow.l | 3 ++ gas/testsuite/gas/bpf/offset16-overflow.s | 4 ++ 19 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 gas/testsuite/gas/bpf/disp16-overflow-relax.d create mode 100644 gas/testsuite/gas/bpf/disp16-overflow-relax.l create mode 100644 gas/testsuite/gas/bpf/disp16-overflow-relax.s create mode 100644 gas/testsuite/gas/bpf/disp16-overflow.d create mode 100644 gas/testsuite/gas/bpf/disp16-overflow.l create mode 100644 gas/testsuite/gas/bpf/disp16-overflow.s create mode 100644 gas/testsuite/gas/bpf/disp32-overflow.d create mode 100644 gas/testsuite/gas/bpf/disp32-overflow.l create mode 100644 gas/testsuite/gas/bpf/disp32-overflow.s create mode 100644 gas/testsuite/gas/bpf/imm32-overflow.d create mode 100644 gas/testsuite/gas/bpf/imm32-overflow.l create mode 100644 gas/testsuite/gas/bpf/imm32-overflow.s create mode 100644 gas/testsuite/gas/bpf/jump-relax-jump-be.d create mode 100644 gas/testsuite/gas/bpf/offset16-overflow.d create mode 100644 gas/testsuite/gas/bpf/offset16-overflow.l create mode 100644 gas/testsuite/gas/bpf/offset16-overflow.s diff --git a/gas/ChangeLog b/gas/ChangeLog index 1f3735d..ab139a9 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,28 @@ +2023-07-30 Jose E. Marchesi + + * 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. + 2023-07-28 Jose E. Marchesi PR gas/30690 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; diff --git a/gas/testsuite/gas/bpf/bpf.exp b/gas/testsuite/gas/bpf/bpf.exp index 6e6a000..80f5a1d 100644 --- a/gas/testsuite/gas/bpf/bpf.exp +++ b/gas/testsuite/gas/bpf/bpf.exp @@ -65,4 +65,11 @@ if {[istarget bpf*-*-*]} { run_dump_test jump-relax-ja-be run_dump_test jump-relax-jump-be + + # Overflow tests + run_dump_test offset16-overflow + run_dump_test disp16-overflow + run_dump_test disp16-overflow-relax + run_dump_test disp32-overflow + run_dump_test imm32-overflow } diff --git a/gas/testsuite/gas/bpf/disp16-overflow-relax.d b/gas/testsuite/gas/bpf/disp16-overflow-relax.d new file mode 100644 index 0000000..051e2fa --- /dev/null +++ b/gas/testsuite/gas/bpf/disp16-overflow-relax.d @@ -0,0 +1,3 @@ +#as: -EL -misa-spec=v4 +#source: disp16-overflow-relax.s +#error_output: disp16-overflow-relax.l diff --git a/gas/testsuite/gas/bpf/disp16-overflow-relax.l b/gas/testsuite/gas/bpf/disp16-overflow-relax.l new file mode 100644 index 0000000..ca572cb --- /dev/null +++ b/gas/testsuite/gas/bpf/disp16-overflow-relax.l @@ -0,0 +1,3 @@ +.*: Assembler messages: +.*:2: Error: signed instruction operand out of range, shall fit in 32 bits +.*:4: Error: signed instruction operand out of range, shall fit in 32 bits diff --git a/gas/testsuite/gas/bpf/disp16-overflow-relax.s b/gas/testsuite/gas/bpf/disp16-overflow-relax.s new file mode 100644 index 0000000..3953992 --- /dev/null +++ b/gas/testsuite/gas/bpf/disp16-overflow-relax.s @@ -0,0 +1,4 @@ + jeq %r1,%r2,2147483647 + jlt %r3,%r4,2147483648 ; Overflows. + jge %r5,10,-2147483648 + ja -2147483649 ; Overflows. diff --git a/gas/testsuite/gas/bpf/disp16-overflow.d b/gas/testsuite/gas/bpf/disp16-overflow.d new file mode 100644 index 0000000..5a0094f --- /dev/null +++ b/gas/testsuite/gas/bpf/disp16-overflow.d @@ -0,0 +1,3 @@ +#as: -EL -mno-relax +#source: disp16-overflow.s +#error_output: disp16-overflow.l diff --git a/gas/testsuite/gas/bpf/disp16-overflow.l b/gas/testsuite/gas/bpf/disp16-overflow.l new file mode 100644 index 0000000..6404b1b --- /dev/null +++ b/gas/testsuite/gas/bpf/disp16-overflow.l @@ -0,0 +1,3 @@ +.*: Assembler messages: +.*:2: Error: signed pc-relative offset out of range, shall fit in 16 bits +.*:4: Error: signed pc-relative offset out of range, shall fit in 16 bits diff --git a/gas/testsuite/gas/bpf/disp16-overflow.s b/gas/testsuite/gas/bpf/disp16-overflow.s new file mode 100644 index 0000000..ab66753 --- /dev/null +++ b/gas/testsuite/gas/bpf/disp16-overflow.s @@ -0,0 +1,4 @@ + ja 32767 + jeq %r1,%r2,32768 ; Overflows + jlt %r3,%r4,-32768 + jge %r5,10,-32769 ; Overflows diff --git a/gas/testsuite/gas/bpf/disp32-overflow.d b/gas/testsuite/gas/bpf/disp32-overflow.d new file mode 100644 index 0000000..09fa189 --- /dev/null +++ b/gas/testsuite/gas/bpf/disp32-overflow.d @@ -0,0 +1,3 @@ +#as: -EL +#source: disp32-overflow.s +#error_output: disp32-overflow.l diff --git a/gas/testsuite/gas/bpf/disp32-overflow.l b/gas/testsuite/gas/bpf/disp32-overflow.l new file mode 100644 index 0000000..8a6b647 --- /dev/null +++ b/gas/testsuite/gas/bpf/disp32-overflow.l @@ -0,0 +1,3 @@ +.*: Assembler messages: +.*:2: Error: signed pc-relative offset out of range, shall fit in 32 bits +.*:4: Error: signed pc-relative offset out of range, shall fit in 32 bits diff --git a/gas/testsuite/gas/bpf/disp32-overflow.s b/gas/testsuite/gas/bpf/disp32-overflow.s new file mode 100644 index 0000000..03a0d97 --- /dev/null +++ b/gas/testsuite/gas/bpf/disp32-overflow.s @@ -0,0 +1,4 @@ + call -2147483648 + call -2147483649 ; This overflows. + call 2147483647 + call 2147483648 ; This overflows. diff --git a/gas/testsuite/gas/bpf/imm32-overflow.d b/gas/testsuite/gas/bpf/imm32-overflow.d new file mode 100644 index 0000000..c8e35a9 --- /dev/null +++ b/gas/testsuite/gas/bpf/imm32-overflow.d @@ -0,0 +1,3 @@ +#as: -EL +#source: imm32-overflow.s +#error_output: imm32-overflow.l diff --git a/gas/testsuite/gas/bpf/imm32-overflow.l b/gas/testsuite/gas/bpf/imm32-overflow.l new file mode 100644 index 0000000..f6691c4 --- /dev/null +++ b/gas/testsuite/gas/bpf/imm32-overflow.l @@ -0,0 +1,3 @@ +.*: Assembler messages: +.*:2: Error: signed immediate out of range, shall fit in 32 bits +.*:4: Error: signed immediate out of range, shall fit in 32 bits diff --git a/gas/testsuite/gas/bpf/imm32-overflow.s b/gas/testsuite/gas/bpf/imm32-overflow.s new file mode 100644 index 0000000..5cb858c --- /dev/null +++ b/gas/testsuite/gas/bpf/imm32-overflow.s @@ -0,0 +1,4 @@ + add %r1, 2147483647 + or %r2, 2147483648 ; This overflows. + xor %r3, -2147483648 + sub %r4, -2147483649 ; This overflows. diff --git a/gas/testsuite/gas/bpf/jump-relax-jump-be.d b/gas/testsuite/gas/bpf/jump-relax-jump-be.d new file mode 100644 index 0000000..5626d56 --- /dev/null +++ b/gas/testsuite/gas/bpf/jump-relax-jump-be.d @@ -0,0 +1,25 @@ +#as: -EB -mdialect=normal +#objdump: -dr -M dec +#source: jump-relax-jump.s +#name: Relaxation of conditional branch instructions, big-endian + +.*: +file format .*bpf.* + +Disassembly of section .text: + +0+ <.*>: + 0: 1d 12 80 00 00 00 00 00 jeq %r1,%r2,-32768 + 8: ad 12 7f ff 00 00 00 00 jlt %r1,%r2,32767 + 10: bd 12 ff fd 00 00 00 00 jle %r1,%r2,-3 + 18: 3d 12 00 01 00 00 00 00 jge %r1,%r2,1 + 20: 05 00 00 01 00 00 00 00 ja 1 + 28: 06 00 00 00 ff ff 7f ff jal -32769 + 30: 2d 12 00 01 00 00 00 00 jgt %r1,%r2,1 + 38: 05 00 00 01 00 00 00 00 ja 1 + 40: 06 00 00 00 00 00 80 00 jal 32768 + 48: 1d 12 00 01 00 00 00 00 jeq %r1,%r2,1 + 50: 05 00 00 01 00 00 00 00 ja 1 + 58: 06 00 00 00 00 00 80 01 jal 32769 + 60: 2d 12 00 01 00 00 00 00 jgt %r1,%r2,1 + 68: 05 00 00 01 00 00 00 00 ja 1 + 70: 06 00 00 00 00 00 80 01 jal 32769 diff --git a/gas/testsuite/gas/bpf/offset16-overflow.d b/gas/testsuite/gas/bpf/offset16-overflow.d new file mode 100644 index 0000000..102edca --- /dev/null +++ b/gas/testsuite/gas/bpf/offset16-overflow.d @@ -0,0 +1,3 @@ +#as: -EL +#source: offset16-overflow.s +#error_output: offset16-overflow.l diff --git a/gas/testsuite/gas/bpf/offset16-overflow.l b/gas/testsuite/gas/bpf/offset16-overflow.l new file mode 100644 index 0000000..6404b1b --- /dev/null +++ b/gas/testsuite/gas/bpf/offset16-overflow.l @@ -0,0 +1,3 @@ +.*: Assembler messages: +.*:2: Error: signed pc-relative offset out of range, shall fit in 16 bits +.*:4: Error: signed pc-relative offset out of range, shall fit in 16 bits diff --git a/gas/testsuite/gas/bpf/offset16-overflow.s b/gas/testsuite/gas/bpf/offset16-overflow.s new file mode 100644 index 0000000..da9f633 --- /dev/null +++ b/gas/testsuite/gas/bpf/offset16-overflow.s @@ -0,0 +1,4 @@ + ldxh %r2, [%r1 + 32767] + ldxw %r2, [%r1 + 32768] ; This overflows + stxw [%r2 - 32768], %r1 + stxdw [%r2 - 32769], %r1 ; This overflows -- 2.7.4