Commit 87eb0152 authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Alexei Starovoitov

selftests/bpf: track string payload offset as scalar in strobemeta

This change prepares strobemeta for update in callbacks verification
logic. To allow bpf_loop() verification converge when multiple
callback iterations are considered:
- track offset inside strobemeta_payload->payload directly as scalar
  value;
- at each iteration make sure that remaining
  strobemeta_payload->payload capacity is sufficient for execution of
  read_{map,str}_var functions;
- make sure that offset is tracked as unbound scalar between
  iterations, otherwise verifier won't be able infer that bpf_loop
  callback reaches identical states.
Acked-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20231121020701.26440-3-eddyz87@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 977bc146
...@@ -24,9 +24,11 @@ struct task_struct {}; ...@@ -24,9 +24,11 @@ struct task_struct {};
#define STACK_TABLE_EPOCH_SHIFT 20 #define STACK_TABLE_EPOCH_SHIFT 20
#define STROBE_MAX_STR_LEN 1 #define STROBE_MAX_STR_LEN 1
#define STROBE_MAX_CFGS 32 #define STROBE_MAX_CFGS 32
#define READ_MAP_VAR_PAYLOAD_CAP \
((1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN)
#define STROBE_MAX_PAYLOAD \ #define STROBE_MAX_PAYLOAD \
(STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \ (STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \
STROBE_MAX_MAPS * (1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN) STROBE_MAX_MAPS * READ_MAP_VAR_PAYLOAD_CAP)
struct strobe_value_header { struct strobe_value_header {
/* /*
...@@ -355,7 +357,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, ...@@ -355,7 +357,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
size_t idx, void *tls_base, size_t idx, void *tls_base,
struct strobe_value_generic *value, struct strobe_value_generic *value,
struct strobemeta_payload *data, struct strobemeta_payload *data,
void *payload) size_t off)
{ {
void *location; void *location;
uint64_t len; uint64_t len;
...@@ -366,7 +368,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, ...@@ -366,7 +368,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
return 0; return 0;
bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr); len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, value->ptr);
/* /*
* if bpf_probe_read_user_str returns error (<0), due to casting to * if bpf_probe_read_user_str returns error (<0), due to casting to
* unsinged int, it will become big number, so next check is * unsinged int, it will become big number, so next check is
...@@ -378,14 +380,14 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, ...@@ -378,14 +380,14 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg,
return 0; return 0;
data->str_lens[idx] = len; data->str_lens[idx] = len;
return len; return off + len;
} }
static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, static __always_inline uint64_t read_map_var(struct strobemeta_cfg *cfg,
size_t idx, void *tls_base, size_t idx, void *tls_base,
struct strobe_value_generic *value, struct strobe_value_generic *value,
struct strobemeta_payload *data, struct strobemeta_payload *data,
void *payload) size_t off)
{ {
struct strobe_map_descr* descr = &data->map_descrs[idx]; struct strobe_map_descr* descr = &data->map_descrs[idx];
struct strobe_map_raw map; struct strobe_map_raw map;
...@@ -397,11 +399,11 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, ...@@ -397,11 +399,11 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
location = calc_location(&cfg->map_locs[idx], tls_base); location = calc_location(&cfg->map_locs[idx], tls_base);
if (!location) if (!location)
return payload; return off;
bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location);
if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr)) if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr))
return payload; return off;
descr->id = map.id; descr->id = map.id;
descr->cnt = map.cnt; descr->cnt = map.cnt;
...@@ -410,10 +412,10 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, ...@@ -410,10 +412,10 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
data->req_meta_valid = 1; data->req_meta_valid = 1;
} }
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, map.tag); len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.tag);
if (len <= STROBE_MAX_STR_LEN) { if (len <= STROBE_MAX_STR_LEN) {
descr->tag_len = len; descr->tag_len = len;
payload += len; off += len;
} }
#ifdef NO_UNROLL #ifdef NO_UNROLL
...@@ -426,22 +428,22 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, ...@@ -426,22 +428,22 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg,
break; break;
descr->key_lens[i] = 0; descr->key_lens[i] = 0;
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN,
map.entries[i].key); map.entries[i].key);
if (len <= STROBE_MAX_STR_LEN) { if (len <= STROBE_MAX_STR_LEN) {
descr->key_lens[i] = len; descr->key_lens[i] = len;
payload += len; off += len;
} }
descr->val_lens[i] = 0; descr->val_lens[i] = 0;
len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN,
map.entries[i].val); map.entries[i].val);
if (len <= STROBE_MAX_STR_LEN) { if (len <= STROBE_MAX_STR_LEN) {
descr->val_lens[i] = len; descr->val_lens[i] = len;
payload += len; off += len;
} }
} }
return payload; return off;
} }
#ifdef USE_BPF_LOOP #ifdef USE_BPF_LOOP
...@@ -455,14 +457,20 @@ struct read_var_ctx { ...@@ -455,14 +457,20 @@ struct read_var_ctx {
struct strobemeta_payload *data; struct strobemeta_payload *data;
void *tls_base; void *tls_base;
struct strobemeta_cfg *cfg; struct strobemeta_cfg *cfg;
void *payload; size_t payload_off;
/* value gets mutated */ /* value gets mutated */
struct strobe_value_generic *value; struct strobe_value_generic *value;
enum read_type type; enum read_type type;
}; };
static int read_var_callback(__u32 index, struct read_var_ctx *ctx) static int read_var_callback(__u64 index, struct read_var_ctx *ctx)
{ {
/* lose precision info for ctx->payload_off, verifier won't track
* double xor, barrier_var() is needed to force clang keep both xors.
*/
ctx->payload_off ^= index;
barrier_var(ctx->payload_off);
ctx->payload_off ^= index;
switch (ctx->type) { switch (ctx->type) {
case READ_INT_VAR: case READ_INT_VAR:
if (index >= STROBE_MAX_INTS) if (index >= STROBE_MAX_INTS)
...@@ -472,14 +480,18 @@ static int read_var_callback(__u32 index, struct read_var_ctx *ctx) ...@@ -472,14 +480,18 @@ static int read_var_callback(__u32 index, struct read_var_ctx *ctx)
case READ_MAP_VAR: case READ_MAP_VAR:
if (index >= STROBE_MAX_MAPS) if (index >= STROBE_MAX_MAPS)
return 1; return 1;
ctx->payload = read_map_var(ctx->cfg, index, ctx->tls_base, if (ctx->payload_off > sizeof(ctx->data->payload) - READ_MAP_VAR_PAYLOAD_CAP)
ctx->value, ctx->data, ctx->payload); return 1;
ctx->payload_off = read_map_var(ctx->cfg, index, ctx->tls_base,
ctx->value, ctx->data, ctx->payload_off);
break; break;
case READ_STR_VAR: case READ_STR_VAR:
if (index >= STROBE_MAX_STRS) if (index >= STROBE_MAX_STRS)
return 1; return 1;
ctx->payload += read_str_var(ctx->cfg, index, ctx->tls_base, if (ctx->payload_off > sizeof(ctx->data->payload) - STROBE_MAX_STR_LEN)
ctx->value, ctx->data, ctx->payload); return 1;
ctx->payload_off = read_str_var(ctx->cfg, index, ctx->tls_base,
ctx->value, ctx->data, ctx->payload_off);
break; break;
} }
return 0; return 0;
...@@ -501,7 +513,8 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -501,7 +513,8 @@ static void *read_strobe_meta(struct task_struct *task,
pid_t pid = bpf_get_current_pid_tgid() >> 32; pid_t pid = bpf_get_current_pid_tgid() >> 32;
struct strobe_value_generic value = {0}; struct strobe_value_generic value = {0};
struct strobemeta_cfg *cfg; struct strobemeta_cfg *cfg;
void *tls_base, *payload; size_t payload_off;
void *tls_base;
cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid); cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid);
if (!cfg) if (!cfg)
...@@ -509,7 +522,7 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -509,7 +522,7 @@ static void *read_strobe_meta(struct task_struct *task,
data->int_vals_set_mask = 0; data->int_vals_set_mask = 0;
data->req_meta_valid = 0; data->req_meta_valid = 0;
payload = data->payload; payload_off = 0;
/* /*
* we don't have struct task_struct definition, it should be: * we don't have struct task_struct definition, it should be:
* tls_base = (void *)task->thread.fsbase; * tls_base = (void *)task->thread.fsbase;
...@@ -522,7 +535,7 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -522,7 +535,7 @@ static void *read_strobe_meta(struct task_struct *task,
.tls_base = tls_base, .tls_base = tls_base,
.value = &value, .value = &value,
.data = data, .data = data,
.payload = payload, .payload_off = 0,
}; };
int err; int err;
...@@ -540,6 +553,11 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -540,6 +553,11 @@ static void *read_strobe_meta(struct task_struct *task,
err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0); err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0);
if (err != STROBE_MAX_MAPS) if (err != STROBE_MAX_MAPS)
return NULL; return NULL;
payload_off = ctx.payload_off;
/* this should not really happen, here only to satisfy verifer */
if (payload_off > sizeof(data->payload))
payload_off = sizeof(data->payload);
#else #else
#ifdef NO_UNROLL #ifdef NO_UNROLL
#pragma clang loop unroll(disable) #pragma clang loop unroll(disable)
...@@ -555,7 +573,7 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -555,7 +573,7 @@ static void *read_strobe_meta(struct task_struct *task,
#pragma unroll #pragma unroll
#endif /* NO_UNROLL */ #endif /* NO_UNROLL */
for (int i = 0; i < STROBE_MAX_STRS; ++i) { for (int i = 0; i < STROBE_MAX_STRS; ++i) {
payload += read_str_var(cfg, i, tls_base, &value, data, payload); payload_off = read_str_var(cfg, i, tls_base, &value, data, payload_off);
} }
#ifdef NO_UNROLL #ifdef NO_UNROLL
#pragma clang loop unroll(disable) #pragma clang loop unroll(disable)
...@@ -563,7 +581,7 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -563,7 +581,7 @@ static void *read_strobe_meta(struct task_struct *task,
#pragma unroll #pragma unroll
#endif /* NO_UNROLL */ #endif /* NO_UNROLL */
for (int i = 0; i < STROBE_MAX_MAPS; ++i) { for (int i = 0; i < STROBE_MAX_MAPS; ++i) {
payload = read_map_var(cfg, i, tls_base, &value, data, payload); payload_off = read_map_var(cfg, i, tls_base, &value, data, payload_off);
} }
#endif /* USE_BPF_LOOP */ #endif /* USE_BPF_LOOP */
...@@ -571,7 +589,7 @@ static void *read_strobe_meta(struct task_struct *task, ...@@ -571,7 +589,7 @@ static void *read_strobe_meta(struct task_struct *task,
* return pointer right after end of payload, so it's possible to * return pointer right after end of payload, so it's possible to
* calculate exact amount of useful data that needs to be sent * calculate exact amount of useful data that needs to be sent
*/ */
return payload; return &data->payload[payload_off];
} }
SEC("raw_tracepoint/kfree_skb") SEC("raw_tracepoint/kfree_skb")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment