brintos

brintos / linux-shallow public Read only

0
0
Text · 12.3 KiB · c810f4f Raw
419 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Converted from tools/testing/selftests/bpf/verifier/var_off.c */3 4#include <linux/bpf.h>5#include <bpf/bpf_helpers.h>6#include "bpf_misc.h"7 8struct {9	__uint(type, BPF_MAP_TYPE_HASH);10	__uint(max_entries, 1);11	__type(key, long long);12	__type(value, long long);13} map_hash_8b SEC(".maps");14 15SEC("lwt_in")16__description("variable-offset ctx access")17__failure __msg("variable ctx access var_off=(0x0; 0x4)")18__naked void variable_offset_ctx_access(void)19{20	asm volatile ("					\21	/* Get an unknown value */			\22	r2 = *(u32*)(r1 + 0);				\23	/* Make it small and 4-byte aligned */		\24	r2 &= 4;					\25	/* add it to skb.  We now have either &skb->len or\26	 * &skb->pkt_type, but we don't know which	\27	 */						\28	r1 += r2;					\29	/* dereference it */				\30	r0 = *(u32*)(r1 + 0);				\31	exit;						\32"	::: __clobber_all);33}34 35SEC("cgroup/skb")36__description("variable-offset stack read, priv vs unpriv")37__success __failure_unpriv38__msg_unpriv("R2 variable stack access prohibited for !root")39__retval(0)40__naked void stack_read_priv_vs_unpriv(void)41{42	asm volatile ("					\43	/* Fill the top 8 bytes of the stack */		\44	r0 = 0;						\45	*(u64*)(r10 - 8) = r0;				\46	/* Get an unknown value */			\47	r2 = *(u32*)(r1 + 0);				\48	/* Make it small and 4-byte aligned */		\49	r2 &= 4;					\50	r2 -= 8;					\51	/* add it to fp.  We now have either fp-4 or fp-8, but\52	 * we don't know which				\53	 */						\54	r2 += r10;					\55	/* dereference it for a stack read */		\56	r0 = *(u32*)(r2 + 0);				\57	r0 = 0;						\58	exit;						\59"	::: __clobber_all);60}61 62SEC("cgroup/skb")63__description("variable-offset stack read, uninitialized")64__success65__failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root")66__naked void variable_offset_stack_read_uninitialized(void)67{68	asm volatile ("					\69	/* Get an unknown value */			\70	r2 = *(u32*)(r1 + 0);				\71	/* Make it small and 4-byte aligned */		\72	r2 &= 4;					\73	r2 -= 8;					\74	/* add it to fp.  We now have either fp-4 or fp-8, but\75	 * we don't know which				\76	 */						\77	r2 += r10;					\78	/* dereference it for a stack read */		\79	r0 = *(u32*)(r2 + 0);				\80	r0 = 0;						\81	exit;						\82"	::: __clobber_all);83}84 85SEC("socket")86__description("variable-offset stack write, priv vs unpriv")87__success88/* Check that the maximum stack depth is correctly maintained according to the89 * maximum possible variable offset.90 */91__log_level(4) __msg("stack depth 16")92__failure_unpriv93/* Variable stack access is rejected for unprivileged.94 */95__msg_unpriv("R2 variable stack access prohibited for !root")96__retval(0)97__naked void stack_write_priv_vs_unpriv(void)98{99	asm volatile ("                               \100	/* Get an unknown value */                    \101	r2 = *(u32*)(r1 + 0);                         \102	/* Make it small and 8-byte aligned */        \103	r2 &= 8;                                      \104	r2 -= 16;                                     \105	/* Add it to fp. We now have either fp-8 or   \106	 * fp-16, but we don't know which             \107	 */                                           \108	r2 += r10;                                    \109	/* Dereference it for a stack write */        \110	r0 = 0;                                       \111	*(u64*)(r2 + 0) = r0;                         \112	exit;                                         \113"	::: __clobber_all);114}115 116/* Similar to the previous test, but this time also perform a read from the117 * address written to with a variable offset. The read is allowed, showing that,118 * after a variable-offset write, a priviledged program can read the slots that119 * were in the range of that write (even if the verifier doesn't actually know if120 * the slot being read was really written to or not.121 *122 * Despite this test being mostly a superset, the previous test is also kept for123 * the sake of it checking the stack depth in the case where there is no read.124 */125SEC("socket")126__description("variable-offset stack write followed by read")127__success128/* Check that the maximum stack depth is correctly maintained according to the129 * maximum possible variable offset.130 */131__log_level(4) __msg("stack depth 16")132__failure_unpriv133__msg_unpriv("R2 variable stack access prohibited for !root")134__retval(0)135__naked void stack_write_followed_by_read(void)136{137	asm volatile ("					\138	/* Get an unknown value */			\139	r2 = *(u32*)(r1 + 0);				\140	/* Make it small and 8-byte aligned */		\141	r2 &= 8;					\142	r2 -= 16;					\143	/* Add it to fp.  We now have either fp-8 or fp-16, but\144	 * we don't know which				\145	 */						\146	r2 += r10;					\147	/* Dereference it for a stack write */		\148	r0 = 0;						\149	*(u64*)(r2 + 0) = r0;				\150	/* Now read from the address we just wrote. */ \151	r3 = *(u64*)(r2 + 0);				\152	r0 = 0;						\153	exit;						\154"	::: __clobber_all);155}156 157SEC("socket")158__description("variable-offset stack write clobbers spilled regs")159__failure160/* In the priviledged case, dereferencing a spilled-and-then-filled161 * register is rejected because the previous variable offset stack162 * write might have overwritten the spilled pointer (i.e. we lose track163 * of the spilled register when we analyze the write).164 */165__msg("R2 invalid mem access 'scalar'")166__failure_unpriv167/* The unprivileged case is not too interesting; variable168 * stack access is rejected.169 */170__msg_unpriv("R2 variable stack access prohibited for !root")171__naked void stack_write_clobbers_spilled_regs(void)172{173	asm volatile ("					\174	/* Dummy instruction; needed because we need to patch the next one\175	 * and we can't patch the first instruction.	\176	 */						\177	r6 = 0;						\178	/* Make R0 a map ptr */				\179	r0 = %[map_hash_8b] ll;				\180	/* Get an unknown value */			\181	r2 = *(u32*)(r1 + 0);				\182	/* Make it small and 8-byte aligned */		\183	r2 &= 8;					\184	r2 -= 16;					\185	/* Add it to fp. We now have either fp-8 or fp-16, but\186	 * we don't know which.				\187	 */						\188	r2 += r10;					\189	/* Spill R0(map ptr) into stack */		\190	*(u64*)(r10 - 8) = r0;				\191	/* Dereference the unknown value for a stack write */\192	r0 = 0;						\193	*(u64*)(r2 + 0) = r0;				\194	/* Fill the register back into R2 */		\195	r2 = *(u64*)(r10 - 8);				\196	/* Try to dereference R2 for a memory load */	\197	r0 = *(u64*)(r2 + 8);				\198	exit;						\199"	:200	: __imm_addr(map_hash_8b)201	: __clobber_all);202}203 204SEC("sockops")205__description("indirect variable-offset stack access, unbounded")206__failure __msg("invalid unbounded variable-offset indirect access to stack R4")207__naked void variable_offset_stack_access_unbounded(void)208{209	asm volatile ("					\210	r2 = 6;						\211	r3 = 28;					\212	/* Fill the top 16 bytes of the stack. */	\213	r4 = 0;						\214	*(u64*)(r10 - 16) = r4;				\215	r4 = 0;						\216	*(u64*)(r10 - 8) = r4;				\217	/* Get an unknown value. */			\218	r4 = *(u64*)(r1 + %[bpf_sock_ops_bytes_received]);\219	/* Check the lower bound but don't check the upper one. */\220	if r4 s< 0 goto l0_%=;				\221	/* Point the lower bound to initialized stack. Offset is now in range\222	 * from fp-16 to fp+0x7fffffffffffffef, i.e. max value is unbounded.\223	 */						\224	r4 -= 16;					\225	r4 += r10;					\226	r5 = 8;						\227	/* Dereference it indirectly. */		\228	call %[bpf_getsockopt];				\229l0_%=:	r0 = 0;						\230	exit;						\231"	:232	: __imm(bpf_getsockopt),233	  __imm_const(bpf_sock_ops_bytes_received, offsetof(struct bpf_sock_ops, bytes_received))234	: __clobber_all);235}236 237SEC("lwt_in")238__description("indirect variable-offset stack access, max out of bound")239__failure __msg("invalid variable-offset indirect access to stack R2")240__naked void access_max_out_of_bound(void)241{242	asm volatile ("					\243	/* Fill the top 8 bytes of the stack */		\244	r2 = 0;						\245	*(u64*)(r10 - 8) = r2;				\246	/* Get an unknown value */			\247	r2 = *(u32*)(r1 + 0);				\248	/* Make it small and 4-byte aligned */		\249	r2 &= 4;					\250	r2 -= 8;					\251	/* add it to fp.  We now have either fp-4 or fp-8, but\252	 * we don't know which				\253	 */						\254	r2 += r10;					\255	/* dereference it indirectly */			\256	r1 = %[map_hash_8b] ll;				\257	call %[bpf_map_lookup_elem];			\258	r0 = 0;						\259	exit;						\260"	:261	: __imm(bpf_map_lookup_elem),262	  __imm_addr(map_hash_8b)263	: __clobber_all);264}265 266/* Similar to the test above, but this time check the special case of a267 * zero-sized stack access. We used to have a bug causing crashes for zero-sized268 * out-of-bounds accesses.269 */270SEC("socket")271__description("indirect variable-offset stack access, zero-sized, max out of bound")272__failure __msg("invalid variable-offset indirect access to stack R1")273__naked void zero_sized_access_max_out_of_bound(void)274{275	asm volatile ("                      \276	r0 = 0;                              \277	/* Fill some stack */                \278	*(u64*)(r10 - 16) = r0;              \279	*(u64*)(r10 - 8) = r0;               \280	/* Get an unknown value */           \281	r1 = *(u32*)(r1 + 0);                \282	r1 &= 63;                            \283	r1 += -16;                           \284	/* r1 is now anywhere in [-16,48) */ \285	r1 += r10;                           \286	r2 = 0;                              \287	r3 = 0;                              \288	call %[bpf_probe_read_kernel];       \289	exit;                                \290"	:291	: __imm(bpf_probe_read_kernel)292	: __clobber_all);293}294 295SEC("lwt_in")296__description("indirect variable-offset stack access, min out of bound")297__failure __msg("invalid variable-offset indirect access to stack R2")298__naked void access_min_out_of_bound(void)299{300	asm volatile ("					\301	/* Fill the top 8 bytes of the stack */		\302	r2 = 0;						\303	*(u64*)(r10 - 8) = r2;				\304	/* Get an unknown value */			\305	r2 = *(u32*)(r1 + 0);				\306	/* Make it small and 4-byte aligned */		\307	r2 &= 4;					\308	r2 -= 516;					\309	/* add it to fp.  We now have either fp-516 or fp-512, but\310	 * we don't know which				\311	 */						\312	r2 += r10;					\313	/* dereference it indirectly */			\314	r1 = %[map_hash_8b] ll;				\315	call %[bpf_map_lookup_elem];			\316	r0 = 0;						\317	exit;						\318"	:319	: __imm(bpf_map_lookup_elem),320	  __imm_addr(map_hash_8b)321	: __clobber_all);322}323 324SEC("cgroup/skb")325__description("indirect variable-offset stack access, min_off < min_initialized")326__success327__failure_unpriv __msg_unpriv("R2 variable stack access prohibited for !root")328__naked void access_min_off_min_initialized(void)329{330	asm volatile ("					\331	/* Fill only the top 8 bytes of the stack. */	\332	r2 = 0;						\333	*(u64*)(r10 - 8) = r2;				\334	/* Get an unknown value */			\335	r2 = *(u32*)(r1 + 0);				\336	/* Make it small and 4-byte aligned. */		\337	r2 &= 4;					\338	r2 -= 16;					\339	/* Add it to fp.  We now have either fp-12 or fp-16, but we don't know\340	 * which. fp-16 size 8 is partially uninitialized stack.\341	 */						\342	r2 += r10;					\343	/* Dereference it indirectly. */		\344	r1 = %[map_hash_8b] ll;				\345	call %[bpf_map_lookup_elem];			\346	r0 = 0;						\347	exit;						\348"	:349	: __imm(bpf_map_lookup_elem),350	  __imm_addr(map_hash_8b)351	: __clobber_all);352}353 354SEC("cgroup/skb")355__description("indirect variable-offset stack access, priv vs unpriv")356__success __failure_unpriv357__msg_unpriv("R2 variable stack access prohibited for !root")358__retval(0)359__naked void stack_access_priv_vs_unpriv(void)360{361	asm volatile ("					\362	/* Fill the top 16 bytes of the stack. */	\363	r2 = 0;						\364	*(u64*)(r10 - 16) = r2;				\365	r2 = 0;						\366	*(u64*)(r10 - 8) = r2;				\367	/* Get an unknown value. */			\368	r2 = *(u32*)(r1 + 0);				\369	/* Make it small and 4-byte aligned. */		\370	r2 &= 4;					\371	r2 -= 16;					\372	/* Add it to fp.  We now have either fp-12 or fp-16, we don't know\373	 * which, but either way it points to initialized stack.\374	 */						\375	r2 += r10;					\376	/* Dereference it indirectly. */		\377	r1 = %[map_hash_8b] ll;				\378	call %[bpf_map_lookup_elem];			\379	r0 = 0;						\380	exit;						\381"	:382	: __imm(bpf_map_lookup_elem),383	  __imm_addr(map_hash_8b)384	: __clobber_all);385}386 387SEC("lwt_in")388__description("indirect variable-offset stack access, ok")389__success __retval(0)390__naked void variable_offset_stack_access_ok(void)391{392	asm volatile ("					\393	/* Fill the top 16 bytes of the stack. */	\394	r2 = 0;						\395	*(u64*)(r10 - 16) = r2;				\396	r2 = 0;						\397	*(u64*)(r10 - 8) = r2;				\398	/* Get an unknown value. */			\399	r2 = *(u32*)(r1 + 0);				\400	/* Make it small and 4-byte aligned. */		\401	r2 &= 4;					\402	r2 -= 16;					\403	/* Add it to fp.  We now have either fp-12 or fp-16, we don't know\404	 * which, but either way it points to initialized stack.\405	 */						\406	r2 += r10;					\407	/* Dereference it indirectly. */		\408	r1 = %[map_hash_8b] ll;				\409	call %[bpf_map_lookup_elem];			\410	r0 = 0;						\411	exit;						\412"	:413	: __imm(bpf_map_lookup_elem),414	  __imm_addr(map_hash_8b)415	: __clobber_all);416}417 418char _license[] SEC("license") = "GPL";419