Skip to content

Commit d7a4cb9

Browse files
rdnaAlexei Starovoitov
authored and
Alexei Starovoitov
committed
bpf: Introduce bpf_strtol and bpf_strtoul helpers
Add bpf_strtol and bpf_strtoul to convert a string to long and unsigned long correspondingly. It's similar to user space strtol(3) and strtoul(3) with a few changes to the API: * instead of NUL-terminated C string the helpers expect buffer and buffer length; * resulting long or unsigned long is returned in a separate result-argument; * return value is used to indicate success or failure, on success number of consumed bytes is returned that can be used to identify position to read next if the buffer is expected to contain multiple integers; * instead of *base* argument, *flags* is used that provides base in 5 LSB, other bits are reserved for future use; * number of supported bases is limited. Documentation for the new helpers is provided in bpf.h UAPI. The helpers are made available to BPF_PROG_TYPE_CGROUP_SYSCTL programs to be able to convert string input to e.g. "ulongvec" output. E.g. "net/ipv4/tcp_mem" consists of three ulong integers. They can be parsed by calling to bpf_strtoul three times. Implementation notes: Implementation includes "../../lib/kstrtox.h" to reuse integer parsing functions. It's done exactly same way as fs/proc/base.c already does. Unfortunately existing kstrtoX function can't be used directly since they fail if any invalid character is present right after integer in the string. Existing simple_strtoX functions can't be used either since they're obsolete and don't handle overflow properly. Signed-off-by: Andrey Ignatov <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 57c3bb7 commit d7a4cb9

File tree

4 files changed

+187
-1
lines changed

4 files changed

+187
-1
lines changed

Diff for: include/linux/bpf.h

+2
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,8 @@ extern const struct bpf_func_proto bpf_sk_redirect_map_proto;
989989
extern const struct bpf_func_proto bpf_spin_lock_proto;
990990
extern const struct bpf_func_proto bpf_spin_unlock_proto;
991991
extern const struct bpf_func_proto bpf_get_local_storage_proto;
992+
extern const struct bpf_func_proto bpf_strtol_proto;
993+
extern const struct bpf_func_proto bpf_strtoul_proto;
992994

993995
/* Shared helpers among cBPF and eBPF. */
994996
void bpf_user_rnd_init_once(void);

Diff for: include/uapi/linux/bpf.h

+50-1
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,53 @@ union bpf_attr {
25752575
* **-E2BIG** if the *buf_len* is too big.
25762576
*
25772577
* **-EINVAL** if sysctl is being read.
2578+
*
2579+
* int bpf_strtol(const char *buf, size_t buf_len, u64 flags, long *res)
2580+
* Description
2581+
* Convert the initial part of the string from buffer *buf* of
2582+
* size *buf_len* to a long integer according to the given base
2583+
* and save the result in *res*.
2584+
*
2585+
* The string may begin with an arbitrary amount of white space
2586+
* (as determined by isspace(3)) followed by a single optional '-'
2587+
* sign.
2588+
*
2589+
* Five least significant bits of *flags* encode base, other bits
2590+
* are currently unused.
2591+
*
2592+
* Base must be either 8, 10, 16 or 0 to detect it automatically
2593+
* similar to user space strtol(3).
2594+
* Return
2595+
* Number of characters consumed on success. Must be positive but
2596+
* no more than buf_len.
2597+
*
2598+
* **-EINVAL** if no valid digits were found or unsupported base
2599+
* was provided.
2600+
*
2601+
* **-ERANGE** if resulting value was out of range.
2602+
*
2603+
* int bpf_strtoul(const char *buf, size_t buf_len, u64 flags, unsigned long *res)
2604+
* Description
2605+
* Convert the initial part of the string from buffer *buf* of
2606+
* size *buf_len* to an unsigned long integer according to the
2607+
* given base and save the result in *res*.
2608+
*
2609+
* The string may begin with an arbitrary amount of white space
2610+
* (as determined by isspace(3)).
2611+
*
2612+
* Five least significant bits of *flags* encode base, other bits
2613+
* are currently unused.
2614+
*
2615+
* Base must be either 8, 10, 16 or 0 to detect it automatically
2616+
* similar to user space strtoul(3).
2617+
* Return
2618+
* Number of characters consumed on success. Must be positive but
2619+
* no more than buf_len.
2620+
*
2621+
* **-EINVAL** if no valid digits were found or unsupported base
2622+
* was provided.
2623+
*
2624+
* **-ERANGE** if resulting value was out of range.
25782625
*/
25792626
#define __BPF_FUNC_MAPPER(FN) \
25802627
FN(unspec), \
@@ -2681,7 +2728,9 @@ union bpf_attr {
26812728
FN(sysctl_get_name), \
26822729
FN(sysctl_get_current_value), \
26832730
FN(sysctl_get_new_value), \
2684-
FN(sysctl_set_new_value),
2731+
FN(sysctl_set_new_value), \
2732+
FN(strtol), \
2733+
FN(strtoul),
26852734

26862735
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
26872736
* function eBPF program intends to call

Diff for: kernel/bpf/cgroup.c

+4
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,10 @@ static const struct bpf_func_proto *
10161016
sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
10171017
{
10181018
switch (func_id) {
1019+
case BPF_FUNC_strtol:
1020+
return &bpf_strtol_proto;
1021+
case BPF_FUNC_strtoul:
1022+
return &bpf_strtoul_proto;
10191023
case BPF_FUNC_sysctl_get_name:
10201024
return &bpf_sysctl_get_name_proto;
10211025
case BPF_FUNC_sysctl_get_current_value:

Diff for: kernel/bpf/helpers.c

+131
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include <linux/sched.h>
1919
#include <linux/uidgid.h>
2020
#include <linux/filter.h>
21+
#include <linux/ctype.h>
22+
23+
#include "../../lib/kstrtox.h"
2124

2225
/* If kernel subsystem is allowing eBPF programs to call this function,
2326
* inside its own verifier_ops->get_func_proto() callback it should return
@@ -363,4 +366,132 @@ const struct bpf_func_proto bpf_get_local_storage_proto = {
363366
.arg2_type = ARG_ANYTHING,
364367
};
365368
#endif
369+
370+
#define BPF_STRTOX_BASE_MASK 0x1F
371+
372+
static int __bpf_strtoull(const char *buf, size_t buf_len, u64 flags,
373+
unsigned long long *res, bool *is_negative)
374+
{
375+
unsigned int base = flags & BPF_STRTOX_BASE_MASK;
376+
const char *cur_buf = buf;
377+
size_t cur_len = buf_len;
378+
unsigned int consumed;
379+
size_t val_len;
380+
char str[64];
381+
382+
if (!buf || !buf_len || !res || !is_negative)
383+
return -EINVAL;
384+
385+
if (base != 0 && base != 8 && base != 10 && base != 16)
386+
return -EINVAL;
387+
388+
if (flags & ~BPF_STRTOX_BASE_MASK)
389+
return -EINVAL;
390+
391+
while (cur_buf < buf + buf_len && isspace(*cur_buf))
392+
++cur_buf;
393+
394+
*is_negative = (cur_buf < buf + buf_len && *cur_buf == '-');
395+
if (*is_negative)
396+
++cur_buf;
397+
398+
consumed = cur_buf - buf;
399+
cur_len -= consumed;
400+
if (!cur_len)
401+
return -EINVAL;
402+
403+
cur_len = min(cur_len, sizeof(str) - 1);
404+
memcpy(str, cur_buf, cur_len);
405+
str[cur_len] = '\0';
406+
cur_buf = str;
407+
408+
cur_buf = _parse_integer_fixup_radix(cur_buf, &base);
409+
val_len = _parse_integer(cur_buf, base, res);
410+
411+
if (val_len & KSTRTOX_OVERFLOW)
412+
return -ERANGE;
413+
414+
if (val_len == 0)
415+
return -EINVAL;
416+
417+
cur_buf += val_len;
418+
consumed += cur_buf - str;
419+
420+
return consumed;
421+
}
422+
423+
static int __bpf_strtoll(const char *buf, size_t buf_len, u64 flags,
424+
long long *res)
425+
{
426+
unsigned long long _res;
427+
bool is_negative;
428+
int err;
429+
430+
err = __bpf_strtoull(buf, buf_len, flags, &_res, &is_negative);
431+
if (err < 0)
432+
return err;
433+
if (is_negative) {
434+
if ((long long)-_res > 0)
435+
return -ERANGE;
436+
*res = -_res;
437+
} else {
438+
if ((long long)_res < 0)
439+
return -ERANGE;
440+
*res = _res;
441+
}
442+
return err;
443+
}
444+
445+
BPF_CALL_4(bpf_strtol, const char *, buf, size_t, buf_len, u64, flags,
446+
long *, res)
447+
{
448+
long long _res;
449+
int err;
450+
451+
err = __bpf_strtoll(buf, buf_len, flags, &_res);
452+
if (err < 0)
453+
return err;
454+
if (_res != (long)_res)
455+
return -ERANGE;
456+
*res = _res;
457+
return err;
458+
}
459+
460+
const struct bpf_func_proto bpf_strtol_proto = {
461+
.func = bpf_strtol,
462+
.gpl_only = false,
463+
.ret_type = RET_INTEGER,
464+
.arg1_type = ARG_PTR_TO_MEM,
465+
.arg2_type = ARG_CONST_SIZE,
466+
.arg3_type = ARG_ANYTHING,
467+
.arg4_type = ARG_PTR_TO_LONG,
468+
};
469+
470+
BPF_CALL_4(bpf_strtoul, const char *, buf, size_t, buf_len, u64, flags,
471+
unsigned long *, res)
472+
{
473+
unsigned long long _res;
474+
bool is_negative;
475+
int err;
476+
477+
err = __bpf_strtoull(buf, buf_len, flags, &_res, &is_negative);
478+
if (err < 0)
479+
return err;
480+
if (is_negative)
481+
return -EINVAL;
482+
if (_res != (unsigned long)_res)
483+
return -ERANGE;
484+
*res = _res;
485+
return err;
486+
}
487+
488+
const struct bpf_func_proto bpf_strtoul_proto = {
489+
.func = bpf_strtoul,
490+
.gpl_only = false,
491+
.ret_type = RET_INTEGER,
492+
.arg1_type = ARG_PTR_TO_MEM,
493+
.arg2_type = ARG_CONST_SIZE,
494+
.arg3_type = ARG_ANYTHING,
495+
.arg4_type = ARG_PTR_TO_LONG,
496+
};
366497
#endif

0 commit comments

Comments
 (0)