aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/bpf.h2
-rw-r--r--include/uapi/linux/bpf.h51
-rw-r--r--kernel/bpf/cgroup.c4
-rw-r--r--kernel/bpf/helpers.c131
4 files changed, 187 insertions, 1 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index fd06ada941ad..f15432d90728 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -989,6 +989,8 @@ extern const struct bpf_func_proto bpf_sk_redirect_map_proto;
989extern const struct bpf_func_proto bpf_spin_lock_proto; 989extern const struct bpf_func_proto bpf_spin_lock_proto;
990extern const struct bpf_func_proto bpf_spin_unlock_proto; 990extern const struct bpf_func_proto bpf_spin_unlock_proto;
991extern const struct bpf_func_proto bpf_get_local_storage_proto; 991extern const struct bpf_func_proto bpf_get_local_storage_proto;
992extern const struct bpf_func_proto bpf_strtol_proto;
993extern const struct bpf_func_proto bpf_strtoul_proto;
992 994
993/* Shared helpers among cBPF and eBPF. */ 995/* Shared helpers among cBPF and eBPF. */
994void bpf_user_rnd_init_once(void); 996void bpf_user_rnd_init_once(void);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 89976de909af..c26be24fd5e2 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -2575,6 +2575,53 @@ union bpf_attr {
2575 * **-E2BIG** if the *buf_len* is too big. 2575 * **-E2BIG** if the *buf_len* is too big.
2576 * 2576 *
2577 * **-EINVAL** if sysctl is being read. 2577 * **-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.
2578 */ 2625 */
2579#define __BPF_FUNC_MAPPER(FN) \ 2626#define __BPF_FUNC_MAPPER(FN) \
2580 FN(unspec), \ 2627 FN(unspec), \
@@ -2681,7 +2728,9 @@ union bpf_attr {
2681 FN(sysctl_get_name), \ 2728 FN(sysctl_get_name), \
2682 FN(sysctl_get_current_value), \ 2729 FN(sysctl_get_current_value), \
2683 FN(sysctl_get_new_value), \ 2730 FN(sysctl_get_new_value), \
2684 FN(sysctl_set_new_value), 2731 FN(sysctl_set_new_value), \
2732 FN(strtol), \
2733 FN(strtoul),
2685 2734
2686/* integer value in 'imm' field of BPF_CALL instruction selects which helper 2735/* integer value in 'imm' field of BPF_CALL instruction selects which helper
2687 * function eBPF program intends to call 2736 * function eBPF program intends to call
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index b2adf22139b3..789d4ab2336e 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -1016,6 +1016,10 @@ static const struct bpf_func_proto *
1016sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 1016sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
1017{ 1017{
1018 switch (func_id) { 1018 switch (func_id) {
1019 case BPF_FUNC_strtol:
1020 return &bpf_strtol_proto;
1021 case BPF_FUNC_strtoul:
1022 return &bpf_strtoul_proto;
1019 case BPF_FUNC_sysctl_get_name: 1023 case BPF_FUNC_sysctl_get_name:
1020 return &bpf_sysctl_get_name_proto; 1024 return &bpf_sysctl_get_name_proto;
1021 case BPF_FUNC_sysctl_get_current_value: 1025 case BPF_FUNC_sysctl_get_current_value:
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