Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Ensure that BPF syscall fds are never 0, 1, or 2

Add a simple wrapper for passing an fd and getting a new one >= 3 if it
is one of 0, 1, or 2. There are two primary reasons to make this change:
First, libbpf relies on the assumption a certain BPF fd is never 0 (e.g.
most recently noticed in [0]). Second, Alexei pointed out in [1] that
some environments reset stdin, stdout, and stderr if they notice an
invalid fd at these numbers. To protect against both these cases, switch
all internal BPF syscall wrappers in libbpf to always return an fd >= 3.
We only need to modify the syscall wrappers and not other code that
assumes a valid fd by doing >= 0, to avoid pointless churn, and because
it is still a valid assumption. The cost paid is two additional syscalls
if fd is in range [0, 2].

[0]: e31eec77e4ab ("bpf: selftests: Fix fd cleanup in get_branch_snapshot")
[1]: https://lore.kernel.org/bpf/CAADnVQKVKY8o_3aU8Gzke443+uHa-eGoM0h7W4srChMXU1S4Bg@mail.gmail.com

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Song Liu <songliubraving@fb.com>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20211028063501.2239335-5-memxor@gmail.com

authored by

Kumar Kartikeya Dwivedi and committed by
Alexei Starovoitov
549a6323 585a3571

+46 -13
+22 -13
tools/lib/bpf/bpf.c
··· 65 65 return syscall(__NR_bpf, cmd, attr, size); 66 66 } 67 67 68 + static inline int sys_bpf_fd(enum bpf_cmd cmd, union bpf_attr *attr, 69 + unsigned int size) 70 + { 71 + int fd; 72 + 73 + fd = sys_bpf(cmd, attr, size); 74 + return ensure_good_fd(fd); 75 + } 76 + 68 77 static inline int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size) 69 78 { 70 79 int retries = 5; 71 80 int fd; 72 81 73 82 do { 74 - fd = sys_bpf(BPF_PROG_LOAD, attr, size); 83 + fd = sys_bpf_fd(BPF_PROG_LOAD, attr, size); 75 84 } while (fd < 0 && errno == EAGAIN && retries-- > 0); 76 85 77 86 return fd; ··· 113 104 attr.inner_map_fd = create_attr->inner_map_fd; 114 105 attr.map_extra = create_attr->map_extra; 115 106 116 - fd = sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); 107 + fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, sizeof(attr)); 117 108 return libbpf_err_errno(fd); 118 109 } 119 110 ··· 215 206 attr.numa_node = node; 216 207 } 217 208 218 - fd = sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); 209 + fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, sizeof(attr)); 219 210 return libbpf_err_errno(fd); 220 211 } 221 212 ··· 643 634 memset(&attr, 0, sizeof(attr)); 644 635 attr.pathname = ptr_to_u64((void *)pathname); 645 636 646 - fd = sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr)); 637 + fd = sys_bpf_fd(BPF_OBJ_GET, &attr, sizeof(attr)); 647 638 return libbpf_err_errno(fd); 648 639 } 649 640 ··· 754 745 break; 755 746 } 756 747 proceed: 757 - fd = sys_bpf(BPF_LINK_CREATE, &attr, sizeof(attr)); 748 + fd = sys_bpf_fd(BPF_LINK_CREATE, &attr, sizeof(attr)); 758 749 return libbpf_err_errno(fd); 759 750 } 760 751 ··· 797 788 memset(&attr, 0, sizeof(attr)); 798 789 attr.iter_create.link_fd = link_fd; 799 790 800 - fd = sys_bpf(BPF_ITER_CREATE, &attr, sizeof(attr)); 791 + fd = sys_bpf_fd(BPF_ITER_CREATE, &attr, sizeof(attr)); 801 792 return libbpf_err_errno(fd); 802 793 } 803 794 ··· 955 946 memset(&attr, 0, sizeof(attr)); 956 947 attr.prog_id = id; 957 948 958 - fd = sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr)); 949 + fd = sys_bpf_fd(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr)); 959 950 return libbpf_err_errno(fd); 960 951 } 961 952 ··· 967 958 memset(&attr, 0, sizeof(attr)); 968 959 attr.map_id = id; 969 960 970 - fd = sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr)); 961 + fd = sys_bpf_fd(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr)); 971 962 return libbpf_err_errno(fd); 972 963 } 973 964 ··· 979 970 memset(&attr, 0, sizeof(attr)); 980 971 attr.btf_id = id; 981 972 982 - fd = sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr)); 973 + fd = sys_bpf_fd(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr)); 983 974 return libbpf_err_errno(fd); 984 975 } 985 976 ··· 991 982 memset(&attr, 0, sizeof(attr)); 992 983 attr.link_id = id; 993 984 994 - fd = sys_bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr)); 985 + fd = sys_bpf_fd(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr)); 995 986 return libbpf_err_errno(fd); 996 987 } 997 988 ··· 1022 1013 attr.raw_tracepoint.name = ptr_to_u64(name); 1023 1014 attr.raw_tracepoint.prog_fd = prog_fd; 1024 1015 1025 - fd = sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr)); 1016 + fd = sys_bpf_fd(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr)); 1026 1017 return libbpf_err_errno(fd); 1027 1018 } 1028 1019 ··· 1042 1033 attr.btf_log_buf = ptr_to_u64(log_buf); 1043 1034 } 1044 1035 1045 - fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr)); 1036 + fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, sizeof(attr)); 1046 1037 1047 1038 if (fd < 0 && !do_log && log_buf && log_buf_size) { 1048 1039 do_log = true; ··· 1084 1075 memset(&attr, 0, sizeof(attr)); 1085 1076 attr.enable_stats.type = type; 1086 1077 1087 - fd = sys_bpf(BPF_ENABLE_STATS, &attr, sizeof(attr)); 1078 + fd = sys_bpf_fd(BPF_ENABLE_STATS, &attr, sizeof(attr)); 1088 1079 return libbpf_err_errno(fd); 1089 1080 } 1090 1081
+24
tools/lib/bpf/libbpf_internal.h
··· 13 13 #include <limits.h> 14 14 #include <errno.h> 15 15 #include <linux/err.h> 16 + #include <fcntl.h> 17 + #include <unistd.h> 16 18 #include "libbpf_legacy.h" 17 19 #include "relo_core.h" 18 20 ··· 491 489 static inline bool is_ldimm64_insn(struct bpf_insn *insn) 492 490 { 493 491 return insn->code == (BPF_LD | BPF_IMM | BPF_DW); 492 + } 493 + 494 + /* if fd is stdin, stdout, or stderr, dup to a fd greater than 2 495 + * Takes ownership of the fd passed in, and closes it if calling 496 + * fcntl(fd, F_DUPFD_CLOEXEC, 3). 497 + */ 498 + static inline int ensure_good_fd(int fd) 499 + { 500 + int old_fd = fd, saved_errno; 501 + 502 + if (fd < 0) 503 + return fd; 504 + if (fd < 3) { 505 + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); 506 + saved_errno = errno; 507 + close(old_fd); 508 + if (fd < 0) { 509 + pr_warn("failed to dup FD %d to FD > 2: %d\n", old_fd, -saved_errno); 510 + errno = saved_errno; 511 + } 512 + } 513 + return fd; 494 514 } 495 515 496 516 #endif /* __LIBBPF_LIBBPF_INTERNAL_H */