aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/bpf/helpers.c
diff options
context:
space:
mode:
authorAndrey Ignatov <rdna@fb.com>2019-03-18 20:55:26 -0400
committerAlexei Starovoitov <ast@kernel.org>2019-04-12 16:54:59 -0400
commitd7a4cb9b6705a89937d12c8158a35a3145dc967a (patch)
tree1a412b1d491dcc6aca5032dc2792a2a530e26af3 /kernel/bpf/helpers.c
parent57c3bb725a3dd97d960d7e1cd0845d88de53217f (diff)
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 <rdna@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'kernel/bpf/helpers.c')
-rw-r--r--kernel/bpf/helpers.c131
1 files changed, 131 insertions, 0 deletions
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index a411fc17d265..4266ffde07ca 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -18,6 +18,9 @@
18#include <linux/sched.h> 18#include <linux/sched.h>
19#include <linux/uidgid.h> 19#include <linux/uidgid.h>
20#include <linux/filter.h> 20#include <linux/filter.h>
21#include <linux/ctype.h>
22
23#include "../../lib/kstrtox.h"
21 24
22/* If kernel subsystem is allowing eBPF programs to call this function, 25/* If kernel subsystem is allowing eBPF programs to call this function,
23 * inside its own verifier_ops->get_func_proto() callback it should return 26 * 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 = {
363 .arg2_type = ARG_ANYTHING, 366 .arg2_type = ARG_ANYTHING,
364}; 367};
365#endif 368#endif
369
370#define BPF_STRTOX_BASE_MASK 0x1F
371
372static 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
423static 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
445BPF_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
460const 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
470BPF_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
488const 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};
366#endif 497#endif