Skip to content

Commit

Permalink
String: Support longer exact match strings
Browse files Browse the repository at this point in the history
String matching is limited to strings of length 144 due to the way the
hash look up tables were implemented (6 key sizes, 24 byte increments).

This commit adds maps to support larger key/string sizes: 256, 512,
1024, 2048, and 4096. This will therefore permit exact string matches
for strings of length up to 4096 (not including null terminator).

Includes tests at map limits.

Signed-off-by: Kevin Sheldrake <kevin.sheldrake@isovalent.com>
  • Loading branch information
kevsecurity committed Feb 6, 2024
1 parent 3ea0ca3 commit 1883303
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 85 deletions.
96 changes: 86 additions & 10 deletions bpf/process/string_maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,30 @@
* up are equally padded to the smallest key size that can accommodate them, and then
* looked up in the related map.
*
* The chosen key sizes are 25, 49, 73, 97, 121, 145 (6 maps).
* The chosen key sizes are 25, 49, 73, 97, 121, 145, 258, 514, 1026, 2050, 4098 (11 maps).
* The first 6 are sized for common uses and to minimise the hashing of empty bytes. The
* following 5 maps notionally double in size, with lengths equal to 2^k + 2.
*
* In order to distinguish between character buffers that end in 0s and similar buffers
* that are padded with 0s, each string will be prefixed by its length stored in a
* single byte.
* single byte (for first 6 maps) or as a little endian u16 (latter 5 maps).
*/
#define STRING_MAPS_KEY_INC_SIZE 24
#define STRING_MAPS_SIZE_0 1 * STRING_MAPS_KEY_INC_SIZE + 1
#define STRING_MAPS_SIZE_1 2 * STRING_MAPS_KEY_INC_SIZE + 1
#define STRING_MAPS_SIZE_2 3 * STRING_MAPS_KEY_INC_SIZE + 1
#define STRING_MAPS_SIZE_3 4 * STRING_MAPS_KEY_INC_SIZE + 1
#define STRING_MAPS_SIZE_4 5 * STRING_MAPS_KEY_INC_SIZE + 1
#define STRING_MAPS_SIZE_5 6 * STRING_MAPS_KEY_INC_SIZE + 1
#define STRING_MAPS_SIZE_0 (1 * STRING_MAPS_KEY_INC_SIZE + 1)
#define STRING_MAPS_SIZE_1 (2 * STRING_MAPS_KEY_INC_SIZE + 1)
#define STRING_MAPS_SIZE_2 (3 * STRING_MAPS_KEY_INC_SIZE + 1)
#define STRING_MAPS_SIZE_3 (4 * STRING_MAPS_KEY_INC_SIZE + 1)
#define STRING_MAPS_SIZE_4 (5 * STRING_MAPS_KEY_INC_SIZE + 1)
#define STRING_MAPS_SIZE_5 (6 * STRING_MAPS_KEY_INC_SIZE + 1)
#define STRING_MAPS_SIZE_6 (256 + 2)
#define STRING_MAPS_SIZE_7 (512 + 2)
#define STRING_MAPS_SIZE_8 (1024 + 2)
#define STRING_MAPS_SIZE_9 (2048 + 2)
#define STRING_MAPS_SIZE_10 (4096 + 2)

#define STRING_MAPS_HEAP_SIZE 16384
#define STRING_MAPS_HEAP_MASK (8192 - 1)
#define STRING_MAPS_COPY_MASK 4095

struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
Expand Down Expand Up @@ -124,18 +135,83 @@ struct {
});
} string_maps_5 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, STRING_MAPS_OUTER_MAX_ENTRIES);
__uint(key_size, sizeof(__u32));
__array(
values, struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, __u8[STRING_MAPS_SIZE_6]);
__type(value, __u8);
});
} string_maps_6 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, STRING_MAPS_OUTER_MAX_ENTRIES);
__uint(key_size, sizeof(__u32));
__array(
values, struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, __u8[STRING_MAPS_SIZE_7]);
__type(value, __u8);
});
} string_maps_7 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, STRING_MAPS_OUTER_MAX_ENTRIES);
__uint(key_size, sizeof(__u32));
__array(
values, struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, __u8[STRING_MAPS_SIZE_8]);
__type(value, __u8);
});
} string_maps_8 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, STRING_MAPS_OUTER_MAX_ENTRIES);
__uint(key_size, sizeof(__u32));
__array(
values, struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, __u8[STRING_MAPS_SIZE_9]);
__type(value, __u8);
});
} string_maps_9 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, STRING_MAPS_OUTER_MAX_ENTRIES);
__uint(key_size, sizeof(__u32));
__array(
values, struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1);
__type(key, __u8[STRING_MAPS_SIZE_10]);
__type(value, __u8);
});
} string_maps_10 SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, 512);
__uint(value_size, STRING_MAPS_HEAP_SIZE);
} string_maps_heap SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, 512);
__uint(value_size, STRING_MAPS_HEAP_SIZE);
} string_maps_ro_zero SEC(".maps");

#define STRING_PREFIX_MAX_LENGTH 256
Expand Down
147 changes: 103 additions & 44 deletions bpf/process/types/basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ struct event_config {
* we will need to resize. TBD would be to size these at compile time using
* buffer size information.
*/
#define MAX_STRING 1024
#define MAX_STRING 4096

#ifdef __MULTI_KPROBE
static inline __attribute__((always_inline)) __u32 get_index(void *ctx)
Expand Down Expand Up @@ -470,7 +470,9 @@ copy_strings(char *args, unsigned long arg)
long size;

// probe_read_str() always nul-terminates the string.
size = probe_read_str(&args[4], MAX_STRING, (char *)arg);
// So add one to the length to allow for it. This should
// result in us honouring our MAX_STRING correctly.
size = probe_read_str(&args[4], MAX_STRING + 1, (char *)arg);
if (size <= 1)
return invalid_ty;
// Remove the nul character from end.
Expand Down Expand Up @@ -703,31 +705,99 @@ copy_char_buf(void *ctx, long off, unsigned long arg, int argm,
return __copy_char_buf(ctx, off, arg, bytes, has_max_data(argm), e, data_heap);
}

static inline __attribute__((always_inline)) u16
string_padded_len(u16 len)
{
u16 padded_len = len;

if (len < STRING_MAPS_SIZE_5) {
if (len % STRING_MAPS_KEY_INC_SIZE != 0)
padded_len = ((len / STRING_MAPS_KEY_INC_SIZE) + 1) * STRING_MAPS_KEY_INC_SIZE;
return padded_len;
}
if (len <= STRING_MAPS_SIZE_6 - 2)
return STRING_MAPS_SIZE_6 - 2;
if (len <= STRING_MAPS_SIZE_7 - 2)
return STRING_MAPS_SIZE_7 - 2;
if (len <= STRING_MAPS_SIZE_8 - 2)
return STRING_MAPS_SIZE_8 - 2;
if (len <= STRING_MAPS_SIZE_9 - 2)
return STRING_MAPS_SIZE_9 - 2;
return STRING_MAPS_SIZE_10 - 2;
}

static inline __attribute__((always_inline)) int
string_map_index(u16 padded_len)
{
if (padded_len < STRING_MAPS_SIZE_5) {
return (padded_len / STRING_MAPS_KEY_INC_SIZE) - 1;
}
switch (padded_len) {
case STRING_MAPS_SIZE_6 - 2:
return 6;
case STRING_MAPS_SIZE_7 - 2:
return 7;
case STRING_MAPS_SIZE_8 - 2:
return 8;
case STRING_MAPS_SIZE_9 - 2:
return 9;
}
return 10;
}

static inline __attribute__((always_inline)) void *
get_string_map(int index, __u32 map_idx)
{
switch (index) {
case 0:
return map_lookup_elem(&string_maps_0, &map_idx);
case 1:
return map_lookup_elem(&string_maps_1, &map_idx);
case 2:
return map_lookup_elem(&string_maps_2, &map_idx);
case 3:
return map_lookup_elem(&string_maps_3, &map_idx);
case 4:
return map_lookup_elem(&string_maps_4, &map_idx);
case 5:
return map_lookup_elem(&string_maps_5, &map_idx);
case 6:
return map_lookup_elem(&string_maps_6, &map_idx);
case 7:
return map_lookup_elem(&string_maps_7, &map_idx);
case 8:
return map_lookup_elem(&string_maps_8, &map_idx);
case 9:
return map_lookup_elem(&string_maps_9, &map_idx);
case 10:
return map_lookup_elem(&string_maps_10, &map_idx);
}
return 0;
}

static inline __attribute__((always_inline)) long
filter_char_buf_equal(struct selector_arg_filter *filter, char *arg_str, uint orig_len)
{
__u32 *map_ids = (__u32 *)&filter->value;
char *heap, *zero_heap;
void *string_map;
__u16 padded_len;
__u32 map_idx;
__u8 len;
__u8 padded_len;
int index;
char *heap, *zero_heap;
int zero = 0;
__u16 len;
int index;

if (orig_len >= STRING_MAPS_SIZE_5 || !orig_len)
if (orig_len > STRING_MAPS_SIZE_10 - 2 || !orig_len)
return 0;

len = (__u8)orig_len;
len = (__u16)orig_len;
// Calculate padded string length
padded_len = len;
if (len % STRING_MAPS_KEY_INC_SIZE != 0)
padded_len = ((len / STRING_MAPS_KEY_INC_SIZE) + 1) * STRING_MAPS_KEY_INC_SIZE;
padded_len = string_padded_len(len);

// Check if we have entries for this padded length.
// Do this before we copy data for efficiency.
index = (padded_len / STRING_MAPS_KEY_INC_SIZE) - 1;
map_idx = map_ids[index & 0x7];
index = string_map_index(padded_len);
map_idx = map_ids[index & 0xf];
if (map_idx == 0xffffffff)
return 0;

Expand All @@ -736,46 +806,35 @@ filter_char_buf_equal(struct selector_arg_filter *filter, char *arg_str, uint or
if (!heap || !zero_heap)
return 0;

// Copy string to heap, preceded by length
heap[0] = len;
// Copy string to heap, preceded by length -
// u8 for first 6 maps; u16 for latter 5 maps
if (index <= 5)
heap[0] = len;
else {
*(u16 *)heap = len;
}

asm volatile("%[len] &= 0xff;\n"
asm volatile("%[len] &= %1;\n"
: [len] "+r"(len)
:);
probe_read(&heap[1], len, arg_str);
: "i"(STRING_MAPS_HEAP_MASK));
if (index <= 5)
probe_read(&heap[1], len, arg_str);
else
probe_read(&heap[2], len, arg_str);

// Pad string to multiple of key increment size
if (padded_len > len) {
asm volatile("%[len] &= 0xff;\n"
asm volatile("%[len] &= %1;\n"
: [len] "+r"(len)
:);
probe_read(heap + len + 1, (padded_len - len) & 0xff, zero_heap);
: "i"(STRING_MAPS_HEAP_MASK));
if (index <= 5)
probe_read(heap + len + 1, (padded_len - len) & STRING_MAPS_COPY_MASK, zero_heap);
else
probe_read(heap + len + 2, (padded_len - len) & STRING_MAPS_COPY_MASK, zero_heap);
}

// Get map for this string length
switch (index) {
case 0:
string_map = map_lookup_elem(&string_maps_0, &map_idx);
break;
case 1:
string_map = map_lookup_elem(&string_maps_1, &map_idx);
break;
case 2:
string_map = map_lookup_elem(&string_maps_2, &map_idx);
break;
case 3:
string_map = map_lookup_elem(&string_maps_3, &map_idx);
break;
case 4:
string_map = map_lookup_elem(&string_maps_4, &map_idx);
break;
case 5:
string_map = map_lookup_elem(&string_maps_5, &map_idx);
break;
default:
return 0;
}

string_map = get_string_map(index, map_idx);
if (!string_map)
return 0;

Expand Down
Loading

0 comments on commit 1883303

Please sign in to comment.