Pull bpf fixes from Alexei Starovoitov: - Fix how linked registers track zero extension of subregisters (Daniel Borkmann) - Fix unsound scalar fork for OR instructions (Daniel Wade) - Fix exception exit lock check for subprogs (Ihor Solodrai) - Fix undefined behavior in interpreter for SDIV/SMOD instructions (Jenny Guanni Qu) - Release module's BTF when module is unloaded (Kumar Kartikeya Dwivedi) - Fix constant blinding for PROBE_MEM32 instructions (Sachin Kumar) - Reset register ID for END instructions to prevent incorrect value tracking (Yazhou Tang) * tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf: selftests/bpf: Add a test cases for sync_linked_regs regarding zext propagation bpf: Fix sync_linked_regs regarding BPF_ADD_CONST32 zext propagation selftests/bpf: Add tests for maybe_fork_scalars() OR vs AND handling bpf: Fix unsound scalar forking in maybe_fork_scalars() for BPF_OR selftests/bpf: Add tests for sdiv32/smod32 with INT_MIN dividend bpf: Fix undefined behavior in interpreter sdiv/smod for INT_MIN selftests/bpf: Add tests for bpf_throw lock leak from subprogs bpf: Fix exception exit lock checking for subprogs bpf: Release module BTF IDR before module unload selftests/bpf: Fix pkg-config call on static builds bpf: Fix constant blinding for PROBE_MEM32 stores selftests/bpf: Add test for BPF_END register ID reset bpf: Reset register ID for BPF_END value tracking
Unified · Split
| @@ -1787,7 +1787,16 @@ static void btf_free_id(struct btf *btf) · 1786 unmodified lines | |||
| 1787 | * of the _bh() version. | 1787 | * of the _bh() version. |
| 1788 | */ | 1788 | */ |
| 1789 | spin_lock_irqsave(&btf_idr_lock, flags); | 1789 | spin_lock_irqsave(&btf_idr_lock, flags); |
| 1790 | idr_remove(&btf_idr, btf->id); | 1790 | if (btf->id) { |
| 1791 | idr_remove(&btf_idr, btf->id); | ||
| 1792 | /* | ||
| 1793 | * Clear the id here to make this function idempotent, since it will get | ||
| 1794 | * called a couple of times for module BTFs: on module unload, and then | ||
| 1795 | * the final btf_put(). btf_alloc_id() starts IDs with 1, so we can use | ||
| 1796 | * 0 as sentinel value. | ||
| 1797 | */ | ||
| 1798 | WRITE_ONCE(btf->id, 0); | ||
| 1799 | } | ||
| 1791 | spin_unlock_irqrestore(&btf_idr_lock, flags); | 1800 | spin_unlock_irqrestore(&btf_idr_lock, flags); |
| 1792 | } | 1801 | } |
| 1793 | 1802 | ||
| @@ -8115,7 +8124,7 @@ static void bpf_btf_show_fdinfo(struct seq_file *m, struct file *filp) · 6321 unmodified lines | |||
| 8115 | { | 8124 | { |
| 8116 | const struct btf *btf = filp->private_data; | 8125 | const struct btf *btf = filp->private_data; |
| 8117 | 8126 | ||
| 8118 | seq_printf(m, "btf_id:\t%u\n", btf->id); | 8127 | seq_printf(m, "btf_id:\t%u\n", READ_ONCE(btf->id)); |
| 8119 | } | 8128 | } |
| 8120 | #endif | 8129 | #endif |
| 8121 | 8130 | ||
| @@ -8197,7 +8206,7 @@ int btf_get_info_by_fd(const struct btf *btf, · 75 unmodified lines | |||
| 8197 | if (copy_from_user(&info, uinfo, info_copy)) | 8206 | if (copy_from_user(&info, uinfo, info_copy)) |
| 8198 | return -EFAULT; | 8207 | return -EFAULT; |
| 8199 | 8208 | ||
| 8200 | info.id = btf->id; | 8209 | info.id = READ_ONCE(btf->id); |
| 8201 | ubtf = u64_to_user_ptr(info.btf); | 8210 | ubtf = u64_to_user_ptr(info.btf); |
| 8202 | btf_copy = min_t(u32, btf->data_size, info.btf_size); | 8211 | btf_copy = min_t(u32, btf->data_size, info.btf_size); |
| 8203 | if (copy_to_user(ubtf, btf->data, btf_copy)) | 8212 | if (copy_to_user(ubtf, btf->data, btf_copy)) |
| @@ -8260,7 +8269,7 @@ int btf_get_fd_by_id(u32 id) · 56 unmodified lines | |||
| 8260 | 8269 | ||
| 8261 | u32 btf_obj_id(const struct btf *btf) | 8270 | u32 btf_obj_id(const struct btf *btf) |
| 8262 | { | 8271 | { |
| 8263 | return btf->id; | 8272 | return READ_ONCE(btf->id); |
| 8264 | } | 8273 | } |
| 8265 | 8274 | ||
| 8266 | bool btf_is_kernel(const struct btf *btf) | 8275 | bool btf_is_kernel(const struct btf *btf) |
| @@ -8382,6 +8391,13 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op, · 115 unmodified lines | |||
| 8382 | if (btf_mod->module != module) | 8391 | if (btf_mod->module != module) |
| 8383 | continue; | 8392 | continue; |
| 8384 | 8393 | ||
| 8394 | /* | ||
| 8395 | * For modules, we do the freeing of BTF IDR as soon as | ||
| 8396 | * module goes away to disable BTF discovery, since the | ||
| 8397 | * btf_try_get_module() on such BTFs will fail. This may | ||
| 8398 | * be called again on btf_put(), but it's ok to do so. | ||
| 8399 | */ | ||
| 8400 | btf_free_id(btf_mod->btf); | ||
| 8385 | list_del(&btf_mod->list); | 8401 | list_del(&btf_mod->list); |
| 8386 | if (btf_mod->sysfs_attr) | 8402 | if (btf_mod->sysfs_attr) |
| 8387 | sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr); | 8403 | sysfs_remove_bin_file(btf_kobj, btf_mod->sysfs_attr); |
| @@ -1422,6 +1422,27 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from, · 1421 unmodified lines | |||
| 1422 | *to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd); | 1422 | *to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd); |
| 1423 | *to++ = BPF_STX_MEM(from->code, from->dst_reg, BPF_REG_AX, from->off); | 1423 | *to++ = BPF_STX_MEM(from->code, from->dst_reg, BPF_REG_AX, from->off); |
| 1424 | break; | 1424 | break; |
| 1425 | |||
| 1426 | case BPF_ST | BPF_PROBE_MEM32 | BPF_DW: | ||
| 1427 | case BPF_ST | BPF_PROBE_MEM32 | BPF_W: | ||
| 1428 | case BPF_ST | BPF_PROBE_MEM32 | BPF_H: | ||
| 1429 | case BPF_ST | BPF_PROBE_MEM32 | BPF_B: | ||
| 1430 | *to++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ | ||
| 1431 | from->imm); | ||
| 1432 | *to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd); | ||
| 1433 | /* | ||
| 1434 | * Cannot use BPF_STX_MEM() macro here as it | ||
| 1435 | * hardcodes BPF_MEM mode, losing PROBE_MEM32 | ||
| 1436 | * and breaking arena addressing in the JIT. | ||
| 1437 | */ | ||
| 1438 | *to++ = (struct bpf_insn) { | ||
| 1439 | .code = BPF_STX | BPF_PROBE_MEM32 | | ||
| 1440 | BPF_SIZE(from->code), | ||
| 1441 | .dst_reg = from->dst_reg, | ||
| 1442 | .src_reg = BPF_REG_AX, | ||
| 1443 | .off = from->off, | ||
| 1444 | }; | ||
| 1445 | break; | ||
| 1425 | } | 1446 | } |
| 1426 | out: | 1447 | out: |
| 1427 | return to - to_buff; | 1448 | return to - to_buff; |
| @@ -1736,6 +1757,12 @@ bool bpf_opcode_in_insntable(u8 code) · 308 unmodified lines | |||
| 1736 | } | 1757 | } |
| 1737 | 1758 | ||
| 1738 | #ifndef CONFIG_BPF_JIT_ALWAYS_ON | 1759 | #ifndef CONFIG_BPF_JIT_ALWAYS_ON |
| 1760 | /* Absolute value of s32 without undefined behavior for S32_MIN */ | ||
| 1761 | static u32 abs_s32(s32 x) | ||
| 1762 | { | ||
| 1763 | return x >= 0 ? (u32)x : -(u32)x; | ||
| 1764 | } | ||
| 1765 | |||
| 1739 | /** | 1766 | /** |
| 1740 | * ___bpf_prog_run - run eBPF program on a given context | 1767 | * ___bpf_prog_run - run eBPF program on a given context |
| 1741 | * @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers | 1768 | * @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers |
| @@ -1900,8 +1927,8 @@ select_insn: · 158 unmodified lines | |||
| 1900 | DST = do_div(AX, (u32) SRC); | 1927 | DST = do_div(AX, (u32) SRC); |
| 1901 | break; | 1928 | break; |
| 1902 | case 1: | 1929 | case 1: |
| 1903 | AX = abs((s32)DST); | 1930 | AX = abs_s32((s32)DST); |
| 1904 | AX = do_div(AX, abs((s32)SRC)); | 1931 | AX = do_div(AX, abs_s32((s32)SRC)); |
| 1905 | if ((s32)DST < 0) | 1932 | if ((s32)DST < 0) |
| 1906 | DST = (u32)-AX; | 1933 | DST = (u32)-AX; |
| 1907 | else | 1934 | else |
| @@ -1928,8 +1955,8 @@ select_insn: · 20 unmodified lines | |||
| 1928 | DST = do_div(AX, (u32) IMM); | 1955 | DST = do_div(AX, (u32) IMM); |
| 1929 | break; | 1956 | break; |
| 1930 | case 1: | 1957 | case 1: |
| 1931 | AX = abs((s32)DST); | 1958 | AX = abs_s32((s32)DST); |
| 1932 | AX = do_div(AX, abs((s32)IMM)); | 1959 | AX = do_div(AX, abs_s32((s32)IMM)); |
| 1933 | if ((s32)DST < 0) | 1960 | if ((s32)DST < 0) |
| 1934 | DST = (u32)-AX; | 1961 | DST = (u32)-AX; |
| 1935 | else | 1962 | else |
| @@ -1955,8 +1982,8 @@ select_insn: · 19 unmodified lines | |||
| 1955 | DST = (u32) AX; | 1982 | DST = (u32) AX; |
| 1956 | break; | 1983 | break; |
| 1957 | case 1: | 1984 | case 1: |
| 1958 | AX = abs((s32)DST); | 1985 | AX = abs_s32((s32)DST); |
| 1959 | do_div(AX, abs((s32)SRC)); | 1986 | do_div(AX, abs_s32((s32)SRC)); |
| 1960 | if (((s32)DST < 0) == ((s32)SRC < 0)) | 1987 | if (((s32)DST < 0) == ((s32)SRC < 0)) |
| 1961 | DST = (u32)AX; | 1988 | DST = (u32)AX; |
| 1962 | else | 1989 | else |
| @@ -1982,8 +2009,8 @@ select_insn: · 19 unmodified lines | |||
| 1982 | DST = (u32) AX; | 2009 | DST = (u32) AX; |
| 1983 | break; | 2010 | break; |
| 1984 | case 1: | 2011 | case 1: |
| 1985 | AX = abs((s32)DST); | 2012 | AX = abs_s32((s32)DST); |
| 1986 | do_div(AX, abs((s32)IMM)); | 2013 | do_div(AX, abs_s32((s32)IMM)); |
| 1987 | if (((s32)DST < 0) == ((s32)IMM < 0)) | 2014 | if (((s32)DST < 0) == ((s32)IMM < 0)) |
| 1988 | DST = (u32)AX; | 2015 | DST = (u32)AX; |
| 1989 | else | 2016 | else |
| @@ -15910,6 +15910,13 @@ static void scalar_byte_swap(struct bpf_reg_state *dst_reg, struct bpf_insn *ins · 15909 unmodified lines | |||
| 15910 | /* Apply bswap if alu64 or switch between big-endian and little-endian machines */ | 15910 | /* Apply bswap if alu64 or switch between big-endian and little-endian machines */ |
| 15911 | bool need_bswap = alu64 || (to_le == is_big_endian); | 15911 | bool need_bswap = alu64 || (to_le == is_big_endian); |
| 15912 | 15912 | ||
| 15913 | /* | ||
| 15914 | * If the register is mutated, manually reset its scalar ID to break | ||
| 15915 | * any existing ties and avoid incorrect bounds propagation. | ||
| 15916 | */ | ||
| 15917 | if (need_bswap || insn->imm == 16 || insn->imm == 32) | ||
| 15918 | dst_reg->id = 0; | ||
| 15919 | |||
| 15913 | if (need_bswap) { | 15920 | if (need_bswap) { |
| 15914 | if (insn->imm == 16) | 15921 | if (insn->imm == 16) |
| 15915 | dst_reg->var_off = tnum_bswap16(dst_reg->var_off); | 15922 | dst_reg->var_off = tnum_bswap16(dst_reg->var_off); |
| @@ -15992,7 +15999,7 @@ static int maybe_fork_scalars(struct bpf_verifier_env *env, struct bpf_insn *ins · 76 unmodified lines | |||
| 15992 | else | 15999 | else |
| 15993 | return 0; | 16000 | return 0; |
| 15994 | 16001 | ||
| 15995 | branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false); | 16002 | branch = push_stack(env, env->insn_idx, env->insn_idx, false); |
| 15996 | if (IS_ERR(branch)) | 16003 | if (IS_ERR(branch)) |
| 15997 | return PTR_ERR(branch); | 16004 | return PTR_ERR(branch); |
| 15998 | 16005 | ||
| @@ -17408,6 +17415,12 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s · 1409 unmodified lines | |||
| 17408 | continue; | 17415 | continue; |
| 17409 | if ((reg->id & ~BPF_ADD_CONST) != (known_reg->id & ~BPF_ADD_CONST)) | 17416 | if ((reg->id & ~BPF_ADD_CONST) != (known_reg->id & ~BPF_ADD_CONST)) |
| 17410 | continue; | 17417 | continue; |
| 17418 | /* | ||
| 17419 | * Skip mixed 32/64-bit links: the delta relationship doesn't | ||
| 17420 | * hold across different ALU widths. | ||
| 17421 | */ | ||
| 17422 | if (((reg->id ^ known_reg->id) & BPF_ADD_CONST) == BPF_ADD_CONST) | ||
| 17423 | continue; | ||
| 17411 | if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) || | 17424 | if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) || |
| 17412 | reg->off == known_reg->off) { | 17425 | reg->off == known_reg->off) { |
| 17413 | s32 saved_subreg_def = reg->subreg_def; | 17426 | s32 saved_subreg_def = reg->subreg_def; |
| @@ -17435,7 +17448,7 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s · 21 unmodified lines | |||
| 17435 | scalar32_min_max_add(reg, &fake_reg); | 17448 | scalar32_min_max_add(reg, &fake_reg); |
| 17436 | scalar_min_max_add(reg, &fake_reg); | 17449 | scalar_min_max_add(reg, &fake_reg); |
| 17437 | reg->var_off = tnum_add(reg->var_off, fake_reg.var_off); | 17450 | reg->var_off = tnum_add(reg->var_off, fake_reg.var_off); |
| 17438 | if (known_reg->id & BPF_ADD_CONST32) | 17451 | if ((reg->id | known_reg->id) & BPF_ADD_CONST32) |
| 17439 | zext_32_to_64(reg); | 17452 | zext_32_to_64(reg); |
| 17440 | reg_bounds_sync(reg); | 17453 | reg_bounds_sync(reg); |
| 17441 | } | 17454 | } |
| @@ -19863,11 +19876,14 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold, · 2421 unmodified lines | |||
| 19863 | * Also verify that new value satisfies old value range knowledge. | 19876 | * Also verify that new value satisfies old value range knowledge. |
| 19864 | */ | 19877 | */ |
| 19865 | 19878 | ||
| 19866 | /* ADD_CONST mismatch: different linking semantics */ | 19879 | /* |
| 19867 | if ((rold->id & BPF_ADD_CONST) && !(rcur->id & BPF_ADD_CONST)) | 19880 | * ADD_CONST flags must match exactly: BPF_ADD_CONST32 and |
| 19868 | return false; | 19881 | * BPF_ADD_CONST64 have different linking semantics in |
| 19869 | 19882 | * sync_linked_regs() (alu32 zero-extends, alu64 does not), | |
| 19870 | if (rold->id && !(rold->id & BPF_ADD_CONST) && (rcur->id & BPF_ADD_CONST)) | 19883 | * so pruning across different flag types is unsafe. |
| 19884 | */ | ||
| 19885 | if (rold->id && | ||
| 19886 | (rold->id & BPF_ADD_CONST) != (rcur->id & BPF_ADD_CONST)) | ||
| 19871 | return false; | 19887 | return false; |
| 19872 | 19888 | ||
| 19873 | /* Both have offset linkage: offsets must match */ | 19889 | /* Both have offset linkage: offsets must match */ |
| @@ -20904,7 +20920,8 @@ static int process_bpf_exit_full(struct bpf_verifier_env *env, · 1030 unmodified lines | |||
| 20904 | * state when it exits. | 20920 | * state when it exits. |
| 20905 | */ | 20921 | */ |
| 20906 | int err = check_resource_leak(env, exception_exit, | 20922 | int err = check_resource_leak(env, exception_exit, |
| 20907 | !env->cur_state->curframe, | 20923 | exception_exit || !env->cur_state->curframe, |
| 20924 | exception_exit ? "bpf_throw" : | ||
| 20908 | "BPF_EXIT instruction in main prog"); | 20925 | "BPF_EXIT instruction in main prog"); |
| 20909 | if (err) | 20926 | if (err) |
| 20910 | return err; | 20927 | return err; |
| @@ -409,7 +409,7 @@ $(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids \ · 408 unmodified lines | |||
| 409 | CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" \ | 409 | CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" \ |
| 410 | LIBBPF_INCLUDE=$(HOST_INCLUDE_DIR) \ | 410 | LIBBPF_INCLUDE=$(HOST_INCLUDE_DIR) \ |
| 411 | EXTRA_LDFLAGS='$(SAN_LDFLAGS) $(EXTRA_LDFLAGS)' \ | 411 | EXTRA_LDFLAGS='$(SAN_LDFLAGS) $(EXTRA_LDFLAGS)' \ |
| 412 | HOSTPKG_CONFIG=$(PKG_CONFIG) \ | 412 | HOSTPKG_CONFIG='$(PKG_CONFIG)' \ |
| 413 | OUTPUT=$(HOST_BUILD_DIR)/resolve_btfids/ BPFOBJ=$(HOST_BPFOBJ) | 413 | OUTPUT=$(HOST_BUILD_DIR)/resolve_btfids/ BPFOBJ=$(HOST_BPFOBJ) |
| 414 | 414 | ||
| 415 | # Get Clang's default includes on this system, as opposed to those seen by | 415 | # Get Clang's default includes on this system, as opposed to those seen by |
| @@ -8,6 +8,11 @@ · 7 unmodified lines | |||
| 8 | #include "bpf_experimental.h" | 8 | #include "bpf_experimental.h" |
| 9 | 9 | ||
| 10 | extern void bpf_rcu_read_lock(void) __ksym; | 10 | extern void bpf_rcu_read_lock(void) __ksym; |
| 11 | extern void bpf_rcu_read_unlock(void) __ksym; | ||
| 12 | extern void bpf_preempt_disable(void) __ksym; | ||
| 13 | extern void bpf_preempt_enable(void) __ksym; | ||
| 14 | extern void bpf_local_irq_save(unsigned long *) __ksym; | ||
| 15 | extern void bpf_local_irq_restore(unsigned long *) __ksym; | ||
| 11 | 16 | ||
| 12 | #define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) | 17 | #define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) |
| 13 | 18 | ||
| @@ -131,7 +136,7 @@ int reject_subprog_with_lock(void *ctx) · 117 unmodified lines | |||
| 131 | } | 136 | } |
| 132 | 137 | ||
| 133 | SEC("?tc") | 138 | SEC("?tc") |
| 134 | __failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region") | 139 | __failure __msg("bpf_throw cannot be used inside bpf_rcu_read_lock-ed region") |
| 135 | int reject_with_rcu_read_lock(void *ctx) | 140 | int reject_with_rcu_read_lock(void *ctx) |
| 136 | { | 141 | { |
| 137 | bpf_rcu_read_lock(); | 142 | bpf_rcu_read_lock(); |
| @@ -147,11 +152,13 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx) · 9 unmodified lines | |||
| 147 | } | 152 | } |
| 148 | 153 | ||
| 149 | SEC("?tc") | 154 | SEC("?tc") |
| 150 | __failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region") | 155 | __failure __msg("bpf_throw cannot be used inside bpf_rcu_read_lock-ed region") |
| 151 | int reject_subprog_with_rcu_read_lock(void *ctx) | 156 | int reject_subprog_with_rcu_read_lock(void *ctx) |
| 152 | { | 157 | { |
| 153 | bpf_rcu_read_lock(); | 158 | bpf_rcu_read_lock(); |
| 154 | return throwing_subprog(ctx); | 159 | throwing_subprog(ctx); |
| 160 | bpf_rcu_read_unlock(); | ||
| 161 | return 0; | ||
| 155 | } | 162 | } |
| 156 | 163 | ||
| 157 | static bool rbless(struct bpf_rb_node *n1, const struct bpf_rb_node *n2) | 164 | static bool rbless(struct bpf_rb_node *n1, const struct bpf_rb_node *n2) |
| @@ -346,4 +353,47 @@ int reject_exception_throw_cb_diff(struct __sk_buff *ctx) · 188 unmodified lines | |||
| 346 | return 0; | 353 | return 0; |
| 347 | } | 354 | } |
| 348 | 355 | ||
| 356 | __noinline static int always_throws(void) | ||
| 357 | { | ||
| 358 | bpf_throw(0); | ||
| 359 | return 0; | ||
| 360 | } | ||
| 361 | |||
| 362 | __noinline static int rcu_lock_then_throw(void) | ||
| 363 | { | ||
| 364 | bpf_rcu_read_lock(); | ||
| 365 | bpf_throw(0); | ||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | SEC("?tc") | ||
| 370 | __failure __msg("bpf_throw cannot be used inside bpf_rcu_read_lock-ed region") | ||
| 371 | int reject_subprog_rcu_lock_throw(void *ctx) | ||
| 372 | { | ||
| 373 | rcu_lock_then_throw(); | ||
| 374 | return 0; | ||
| 375 | } | ||
| 376 | |||
| 377 | SEC("?tc") | ||
| 378 | __failure __msg("bpf_throw cannot be used inside bpf_preempt_disable-ed region") | ||
| 379 | int reject_subprog_throw_preempt_lock(void *ctx) | ||
| 380 | { | ||
| 381 | bpf_preempt_disable(); | ||
| 382 | always_throws(); | ||
| 383 | bpf_preempt_enable(); | ||
| 384 | return 0; | ||
| 385 | } | ||
| 386 | |||
| 387 | SEC("?tc") | ||
| 388 | __failure __msg("bpf_throw cannot be used inside bpf_local_irq_save-ed region") | ||
| 389 | int reject_subprog_throw_irq_lock(void *ctx) | ||
| 390 | { | ||
| 391 | unsigned long flags; | ||
| 392 | |||
| 393 | bpf_local_irq_save(&flags); | ||
| 394 | always_throws(); | ||
| 395 | bpf_local_irq_restore(&flags); | ||
| 396 | return 0; | ||
| 397 | } | ||
| 398 | |||
| 349 | char _license[] SEC("license") = "GPL"; | 399 | char _license[] SEC("license") = "GPL"; |
| @@ -2037,4 +2037,98 @@ __naked void signed_unsigned_intersection32_case2(void *ctx) · 2036 unmodified lines | |||
| 2037 | : __clobber_all); | 2037 | : __clobber_all); |
| 2038 | } | 2038 | } |
| 2039 | 2039 | ||
| 2040 | SEC("socket") | ||
| 2041 | __description("maybe_fork_scalars: OR with constant rejects OOB") | ||
| 2042 | __failure __msg("invalid access to map value") | ||
| 2043 | __naked void or_scalar_fork_rejects_oob(void) | ||
| 2044 | { | ||
| 2045 | asm volatile (" \ | ||
| 2046 | r1 = 0; \ | ||
| 2047 | *(u64*)(r10 - 8) = r1; \ | ||
| 2048 | r2 = r10; \ | ||
| 2049 | r2 += -8; \ | ||
| 2050 | r1 = %[map_hash_8b] ll; \ | ||
| 2051 | call %[bpf_map_lookup_elem]; \ | ||
| 2052 | if r0 == 0 goto l0_%=; \ | ||
| 2053 | r9 = r0; \ | ||
| 2054 | r6 = *(u64*)(r9 + 0); \ | ||
| 2055 | r6 s>>= 63; \ | ||
| 2056 | r6 |= 8; \ | ||
| 2057 | /* r6 is -1 (current) or 8 (pushed) */ \ | ||
| 2058 | if r6 s< 0 goto l0_%=; \ | ||
| 2059 | /* pushed path: r6 = 8, OOB for value_size=8 */ \ | ||
| 2060 | r9 += r6; \ | ||
| 2061 | r0 = *(u8*)(r9 + 0); \ | ||
| 2062 | l0_%=: r0 = 0; \ | ||
| 2063 | exit; \ | ||
| 2064 | " : | ||
| 2065 | : __imm(bpf_map_lookup_elem), | ||
| 2066 | __imm_addr(map_hash_8b) | ||
| 2067 | : __clobber_all); | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | SEC("socket") | ||
| 2071 | __description("maybe_fork_scalars: AND with constant still works") | ||
| 2072 | __success __retval(0) | ||
| 2073 | __naked void and_scalar_fork_still_works(void) | ||
| 2074 | { | ||
| 2075 | asm volatile (" \ | ||
| 2076 | r1 = 0; \ | ||
| 2077 | *(u64*)(r10 - 8) = r1; \ | ||
| 2078 | r2 = r10; \ | ||
| 2079 | r2 += -8; \ | ||
| 2080 | r1 = %[map_hash_8b] ll; \ | ||
| 2081 | call %[bpf_map_lookup_elem]; \ | ||
| 2082 | if r0 == 0 goto l0_%=; \ | ||
| 2083 | r9 = r0; \ | ||
| 2084 | r6 = *(u64*)(r9 + 0); \ | ||
| 2085 | r6 s>>= 63; \ | ||
| 2086 | r6 &= 4; \ | ||
| 2087 | /* \ | ||
| 2088 | * r6 is 0 (pushed, 0&4==0) or 4 (current) \ | ||
| 2089 | * both within value_size=8 \ | ||
| 2090 | */ \ | ||
| 2091 | if r6 s< 0 goto l0_%=; \ | ||
| 2092 | r9 += r6; \ | ||
| 2093 | r0 = *(u8*)(r9 + 0); \ | ||
| 2094 | l0_%=: r0 = 0; \ | ||
| 2095 | exit; \ | ||
| 2096 | " : | ||
| 2097 | : __imm(bpf_map_lookup_elem), | ||
| 2098 | __imm_addr(map_hash_8b) | ||
| 2099 | : __clobber_all); | ||
| 2100 | } | ||
| 2101 | |||
| 2102 | SEC("socket") | ||
| 2103 | __description("maybe_fork_scalars: OR with constant allows in-bounds") | ||
| 2104 | __success __retval(0) | ||
| 2105 | __naked void or_scalar_fork_allows_inbounds(void) | ||
| 2106 | { | ||
| 2107 | asm volatile (" \ | ||
| 2108 | r1 = 0; \ | ||
| 2109 | *(u64*)(r10 - 8) = r1; \ | ||
| 2110 | r2 = r10; \ | ||
| 2111 | r2 += -8; \ | ||
| 2112 | r1 = %[map_hash_8b] ll; \ | ||
| 2113 | call %[bpf_map_lookup_elem]; \ | ||
| 2114 | if r0 == 0 goto l0_%=; \ | ||
| 2115 | r9 = r0; \ | ||
| 2116 | r6 = *(u64*)(r9 + 0); \ | ||
| 2117 | r6 s>>= 63; \ | ||
| 2118 | r6 |= 4; \ | ||
| 2119 | /* \ | ||
| 2120 | * r6 is -1 (current) or 4 (pushed) \ | ||
| 2121 | * pushed path: r6 = 4, within value_size=8 \ | ||
| 2122 | */ \ | ||
| 2123 | if r6 s< 0 goto l0_%=; \ | ||
| 2124 | r9 += r6; \ | ||
| 2125 | r0 = *(u8*)(r9 + 0); \ | ||
| 2126 | l0_%=: r0 = 0; \ | ||
| 2127 | exit; \ | ||
| 2128 | " : | ||
| 2129 | : __imm(bpf_map_lookup_elem), | ||
| 2130 | __imm_addr(map_hash_8b) | ||
| 2131 | : __clobber_all); | ||
| 2132 | } | ||
| 2133 | |||
| 2040 | char _license[] SEC("license") = "GPL"; | 2134 | char _license[] SEC("license") = "GPL"; |
| @@ -91,6 +91,28 @@ BSWAP_RANGE_TEST(le32_range, "le32", 0x3f00, 0x3f0000) · 90 unmodified lines | |||
| 91 | BSWAP_RANGE_TEST(le64_range, "le64", 0x3f00, 0x3f000000000000) | 91 | BSWAP_RANGE_TEST(le64_range, "le64", 0x3f00, 0x3f000000000000) |
| 92 | #endif | 92 | #endif |
| 93 | 93 | ||
| 94 | SEC("socket") | ||
| 95 | __description("BSWAP, reset reg id") | ||
| 96 | __failure __msg("math between fp pointer and register with unbounded min value is not allowed") | ||
| 97 | __naked void bswap_reset_reg_id(void) | ||
| 98 | { | ||
| 99 | asm volatile (" \ | ||
| 100 | call %[bpf_ktime_get_ns]; \ | ||
| 101 | r1 = r0; \ | ||
| 102 | r0 = be16 r0; \ | ||
| 103 | if r0 != 1 goto l0_%=; \ | ||
| 104 | r2 = r10; \ | ||
| 105 | r2 += -512; \ | ||
| 106 | r2 += r1; \ | ||
| 107 | *(u8 *)(r2 + 0) = 0; \ | ||
| 108 | l0_%=: \ | ||
| 109 | r0 = 0; \ | ||
| 110 | exit; \ | ||
| 111 | " : | ||
| 112 | : __imm(bpf_ktime_get_ns) | ||
| 113 | : __clobber_all); | ||
| 114 | } | ||
| 115 | |||
| 94 | #else | 116 | #else |
| 95 | 117 | ||
| 96 | SEC("socket") | 118 | SEC("socket") |
| @@ -348,6 +348,114 @@ l0_%=: \ · 347 unmodified lines | |||
| 348 | : __clobber_all); | 348 | : __clobber_all); |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | /* | ||
| 352 | * Test that sync_linked_regs() checks reg->id (the linked target register) | ||
| 353 | * for BPF_ADD_CONST32 rather than known_reg->id (the branch register). | ||
| 354 | */ | ||
| 355 | SEC("socket") | ||
| 356 | __success | ||
| 357 | __naked void scalars_alu32_zext_linked_reg(void) | ||
| 358 | { | ||
| 359 | asm volatile (" \ | ||
| 360 | call %[bpf_get_prandom_u32]; \ | ||
| 361 | w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \ | ||
| 362 | r7 = r6; /* linked: same id as r6 */ \ | ||
| 363 | w7 += 1; /* alu32: r7.id |= BPF_ADD_CONST32 */ \ | ||
| 364 | r8 = 0xFFFFffff ll; \ | ||
| 365 | if r6 < r8 goto l0_%=; \ | ||
| 366 | /* r6 in [0xFFFFFFFF, 0xFFFFFFFF] */ \ | ||
| 367 | /* sync_linked_regs: known_reg=r6, reg=r7 */ \ | ||
| 368 | /* CPU: w7 = (u32)(0xFFFFFFFF + 1) = 0, zext -> r7 = 0 */ \ | ||
| 369 | /* With fix: r7 64-bit = [0, 0] (zext applied) */ \ | ||
| 370 | /* Without fix: r7 64-bit = [0x100000000] (no zext) */ \ | ||
| 371 | r7 >>= 32; \ | ||
| 372 | if r7 == 0 goto l0_%=; \ | ||
| 373 | r0 /= 0; /* unreachable with fix */ \ | ||
| 374 | l0_%=: \ | ||
| 375 | r0 = 0; \ | ||
| 376 | exit; \ | ||
| 377 | " : | ||
| 378 | : __imm(bpf_get_prandom_u32) | ||
| 379 | : __clobber_all); | ||
| 380 | } | ||
| 381 | |||
| 382 | /* | ||
| 383 | * Test that sync_linked_regs() skips propagation when one register used | ||
| 384 | * alu32 (BPF_ADD_CONST32) and the other used alu64 (BPF_ADD_CONST64). | ||
| 385 | * The delta relationship doesn't hold across different ALU widths. | ||
| 386 | */ | ||
| 387 | SEC("socket") | ||
| 388 | __failure __msg("div by zero") | ||
| 389 | __naked void scalars_alu32_alu64_cross_type(void) | ||
| 390 | { | ||
| 391 | asm volatile (" \ | ||
| 392 | call %[bpf_get_prandom_u32]; \ | ||
| 393 | w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \ | ||
| 394 | r7 = r6; /* linked: same id as r6 */ \ | ||
| 395 | w7 += 1; /* alu32: BPF_ADD_CONST32, delta = 1 */ \ | ||
| 396 | r8 = r6; /* linked: same id as r6 */ \ | ||
| 397 | r8 += 2; /* alu64: BPF_ADD_CONST64, delta = 2 */ \ | ||
| 398 | r9 = 0xFFFFffff ll; \ | ||
| 399 | if r7 < r9 goto l0_%=; \ | ||
| 400 | /* r7 = 0xFFFFFFFF */ \ | ||
| 401 | /* sync: known_reg=r7 (ADD_CONST32), reg=r8 (ADD_CONST64) */ \ | ||
| 402 | /* Without fix: r8 = zext(0xFFFFFFFF + 1) = 0 */ \ | ||
| 403 | /* With fix: r8 stays [2, 0x100000001] (r8 >= 2) */ \ | ||
| 404 | if r8 > 0 goto l1_%=; \ | ||
| 405 | goto l0_%=; \ | ||
| 406 | l1_%=: \ | ||
| 407 | r0 /= 0; /* div by zero */ \ | ||
| 408 | l0_%=: \ | ||
| 409 | r0 = 0; \ | ||
| 410 | exit; \ | ||
| 411 | " : | ||
| 412 | : __imm(bpf_get_prandom_u32) | ||
| 413 | : __clobber_all); | ||
| 414 | } | ||
| 415 | |||
| 416 | /* | ||
| 417 | * Test that regsafe() prevents pruning when two paths reach the same program | ||
| 418 | * point with linked registers carrying different ADD_CONST flags (one | ||
| 419 | * BPF_ADD_CONST32 from alu32, another BPF_ADD_CONST64 from alu64). | ||
| 420 | */ | ||
| 421 | SEC("socket") | ||
| 422 | __failure __msg("div by zero") | ||
| 423 | __flag(BPF_F_TEST_STATE_FREQ) | ||
| 424 | __naked void scalars_alu32_alu64_regsafe_pruning(void) | ||
| 425 | { | ||
| 426 | asm volatile (" \ | ||
| 427 | call %[bpf_get_prandom_u32]; \ | ||
| 428 | w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \ | ||
| 429 | r7 = r6; /* linked: same id as r6 */ \ | ||
| 430 | /* Get another random value for the path branch */ \ | ||
| 431 | call %[bpf_get_prandom_u32]; \ | ||
| 432 | if r0 > 0 goto l_pathb_%=; \ | ||
| 433 | /* Path A: alu32 */ \ | ||
| 434 | w7 += 1; /* BPF_ADD_CONST32, delta = 1 */\ | ||
| 435 | goto l_merge_%=; \ | ||
| 436 | l_pathb_%=: \ | ||
| 437 | /* Path B: alu64 */ \ | ||
| 438 | r7 += 1; /* BPF_ADD_CONST64, delta = 1 */\ | ||
| 439 | l_merge_%=: \ | ||
| 440 | /* Merge point: regsafe() compares path B against cached path A. */ \ | ||
| 441 | /* Narrow r6 to trigger sync_linked_regs for r7 */ \ | ||
| 442 | r9 = 0xFFFFffff ll; \ | ||
| 443 | if r6 < r9 goto l0_%=; \ | ||
| 444 | /* r6 = 0xFFFFFFFF */ \ | ||
| 445 | /* sync: r7 = 0xFFFFFFFF + 1 = 0x100000000 */ \ | ||
| 446 | /* Path A: zext -> r7 = 0 */ \ | ||
| 447 | /* Path B: no zext -> r7 = 0x100000000 */ \ | ||
| 448 | r7 >>= 32; \ | ||
| 449 | if r7 == 0 goto l0_%=; \ | ||
| 450 | r0 /= 0; /* div by zero on path B */ \ | ||
| 451 | l0_%=: \ | ||
| 452 | r0 = 0; \ | ||
| 453 | exit; \ | ||
| 454 | " : | ||
| 455 | : __imm(bpf_get_prandom_u32) | ||
| 456 | : __clobber_all); | ||
| 457 | } | ||
| 458 | |||
| 351 | SEC("socket") | 459 | SEC("socket") |
| 352 | __success | 460 | __success |
| 353 | void alu32_negative_offset(void) | 461 | void alu32_negative_offset(void) |
| @@ -1209,6 +1209,64 @@ __naked void smod32_ri_divisor_neg_1(void) · 1208 unmodified lines | |||
| 1209 | : __clobber_all); | 1209 | : __clobber_all); |
| 1210 | } | 1210 | } |
| 1211 | 1211 | ||
| 1212 | SEC("socket") | ||
| 1213 | __description("SDIV32, INT_MIN divided by 2, imm") | ||
| 1214 | __success __success_unpriv __retval(-1073741824) | ||
| 1215 | __naked void sdiv32_int_min_div_2_imm(void) | ||
| 1216 | { | ||
| 1217 | asm volatile (" \ | ||
| 1218 | w0 = %[int_min]; \ | ||
| 1219 | w0 s/= 2; \ | ||
| 1220 | exit; \ | ||
| 1221 | " : | ||
| 1222 | : __imm_const(int_min, INT_MIN) | ||
| 1223 | : __clobber_all); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | SEC("socket") | ||
| 1227 | __description("SDIV32, INT_MIN divided by 2, reg") | ||
| 1228 | __success __success_unpriv __retval(-1073741824) | ||
| 1229 | __naked void sdiv32_int_min_div_2_reg(void) | ||
| 1230 | { | ||
| 1231 | asm volatile (" \ | ||
| 1232 | w0 = %[int_min]; \ | ||
| 1233 | w1 = 2; \ | ||
| 1234 | w0 s/= w1; \ | ||
| 1235 | exit; \ | ||
| 1236 | " : | ||
| 1237 | : __imm_const(int_min, INT_MIN) | ||
| 1238 | : __clobber_all); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | SEC("socket") | ||
| 1242 | __description("SMOD32, INT_MIN modulo 2, imm") | ||
| 1243 | __success __success_unpriv __retval(0) | ||
| 1244 | __naked void smod32_int_min_mod_2_imm(void) | ||
| 1245 | { | ||
| 1246 | asm volatile (" \ | ||
| 1247 | w0 = %[int_min]; \ | ||
| 1248 | w0 s%%= 2; \ | ||
| 1249 | exit; \ | ||
| 1250 | " : | ||
| 1251 | : __imm_const(int_min, INT_MIN) | ||
| 1252 | : __clobber_all); | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | SEC("socket") | ||
| 1256 | __description("SMOD32, INT_MIN modulo -2, imm") | ||
| 1257 | __success __success_unpriv __retval(0) | ||
| 1258 | __naked void smod32_int_min_mod_neg2_imm(void) | ||
| 1259 | { | ||
| 1260 | asm volatile (" \ | ||
| 1261 | w0 = %[int_min]; \ | ||
| 1262 | w0 s%%= -2; \ | ||
| 1263 | exit; \ | ||
| 1264 | " : | ||
| 1265 | : __imm_const(int_min, INT_MIN) | ||
| 1266 | : __clobber_all); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | |||
| 1212 | #else | 1270 | #else |
| 1213 | 1271 | ||
| 1214 | SEC("socket") | 1272 | SEC("socket") |