mirror of https://gitee.com/openkylin/linux.git
selftests/bpf: Make sure optvals > PAGE_SIZE are bypassed
We are relying on the fact, that we can pass > sizeof(int) optvals to the SOL_IP+IP_FREEBIND option (the kernel will take first 4 bytes). In the BPF program we check that we can only touch PAGE_SIZE bytes, but the real optlen is PAGE_SIZE * 2. In both cases, we override it to some predefined value and trim the optlen. Also, let's modify exiting IP_TOS usecase to test optlen=0 case where BPF program just bypasses the data as is. Signed-off-by: Stanislav Fomichev <sdf@google.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/20200617010416.93086-2-sdf@google.com
This commit is contained in:
parent
d8fe449a9c
commit
a0cb12b031
|
@ -13,6 +13,7 @@ static int getsetsockopt(void)
|
||||||
char cc[16]; /* TCP_CA_NAME_MAX */
|
char cc[16]; /* TCP_CA_NAME_MAX */
|
||||||
} buf = {};
|
} buf = {};
|
||||||
socklen_t optlen;
|
socklen_t optlen;
|
||||||
|
char *big_buf = NULL;
|
||||||
|
|
||||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
|
@ -22,24 +23,31 @@ static int getsetsockopt(void)
|
||||||
|
|
||||||
/* IP_TOS - BPF bypass */
|
/* IP_TOS - BPF bypass */
|
||||||
|
|
||||||
buf.u8[0] = 0x08;
|
optlen = getpagesize() * 2;
|
||||||
err = setsockopt(fd, SOL_IP, IP_TOS, &buf, 1);
|
big_buf = calloc(1, optlen);
|
||||||
|
if (!big_buf) {
|
||||||
|
log_err("Couldn't allocate two pages");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(int *)big_buf = 0x08;
|
||||||
|
err = setsockopt(fd, SOL_IP, IP_TOS, big_buf, optlen);
|
||||||
if (err) {
|
if (err) {
|
||||||
log_err("Failed to call setsockopt(IP_TOS)");
|
log_err("Failed to call setsockopt(IP_TOS)");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.u8[0] = 0x00;
|
memset(big_buf, 0, optlen);
|
||||||
optlen = 1;
|
optlen = 1;
|
||||||
err = getsockopt(fd, SOL_IP, IP_TOS, &buf, &optlen);
|
err = getsockopt(fd, SOL_IP, IP_TOS, big_buf, &optlen);
|
||||||
if (err) {
|
if (err) {
|
||||||
log_err("Failed to call getsockopt(IP_TOS)");
|
log_err("Failed to call getsockopt(IP_TOS)");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf.u8[0] != 0x08) {
|
if (*(int *)big_buf != 0x08) {
|
||||||
log_err("Unexpected getsockopt(IP_TOS) buf[0] 0x%02x != 0x08",
|
log_err("Unexpected getsockopt(IP_TOS) optval 0x%x != 0x08",
|
||||||
buf.u8[0]);
|
*(int *)big_buf);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +86,28 @@ static int getsetsockopt(void)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* IP_FREEBIND - BPF can't access optval past PAGE_SIZE */
|
||||||
|
|
||||||
|
optlen = getpagesize() * 2;
|
||||||
|
memset(big_buf, 0, optlen);
|
||||||
|
|
||||||
|
err = setsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, optlen);
|
||||||
|
if (err != 0) {
|
||||||
|
log_err("Failed to call setsockopt, ret=%d", err);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = getsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, &optlen);
|
||||||
|
if (err != 0) {
|
||||||
|
log_err("Failed to call getsockopt, ret=%d", err);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optlen != 1 || *(__u8 *)big_buf != 0x55) {
|
||||||
|
log_err("Unexpected IP_FREEBIND getsockopt, optlen=%d, optval=0x%x",
|
||||||
|
optlen, *(__u8 *)big_buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* SO_SNDBUF is overwritten */
|
/* SO_SNDBUF is overwritten */
|
||||||
|
|
||||||
buf.u32 = 0x01010101;
|
buf.u32 = 0x01010101;
|
||||||
|
@ -124,9 +154,11 @@ static int getsetsockopt(void)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(big_buf);
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
|
free(big_buf);
|
||||||
close(fd);
|
close(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
__u32 _version SEC("version") = 1;
|
__u32 _version SEC("version") = 1;
|
||||||
|
|
||||||
|
#ifndef PAGE_SIZE
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SOL_CUSTOM 0xdeadbeef
|
#define SOL_CUSTOM 0xdeadbeef
|
||||||
|
|
||||||
struct sockopt_sk {
|
struct sockopt_sk {
|
||||||
|
@ -28,12 +32,14 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
||||||
__u8 *optval = ctx->optval;
|
__u8 *optval = ctx->optval;
|
||||||
struct sockopt_sk *storage;
|
struct sockopt_sk *storage;
|
||||||
|
|
||||||
if (ctx->level == SOL_IP && ctx->optname == IP_TOS)
|
if (ctx->level == SOL_IP && ctx->optname == IP_TOS) {
|
||||||
/* Not interested in SOL_IP:IP_TOS;
|
/* Not interested in SOL_IP:IP_TOS;
|
||||||
* let next BPF program in the cgroup chain or kernel
|
* let next BPF program in the cgroup chain or kernel
|
||||||
* handle it.
|
* handle it.
|
||||||
*/
|
*/
|
||||||
|
ctx->optlen = 0; /* bypass optval>PAGE_SIZE */
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
|
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
|
||||||
/* Not interested in SOL_SOCKET:SO_SNDBUF;
|
/* Not interested in SOL_SOCKET:SO_SNDBUF;
|
||||||
|
@ -51,6 +57,26 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
|
||||||
|
if (optval + 1 > optval_end)
|
||||||
|
return 0; /* EPERM, bounds check */
|
||||||
|
|
||||||
|
ctx->retval = 0; /* Reset system call return value to zero */
|
||||||
|
|
||||||
|
/* Always export 0x55 */
|
||||||
|
optval[0] = 0x55;
|
||||||
|
ctx->optlen = 1;
|
||||||
|
|
||||||
|
/* Userspace buffer is PAGE_SIZE * 2, but BPF
|
||||||
|
* program can only see the first PAGE_SIZE
|
||||||
|
* bytes of data.
|
||||||
|
*/
|
||||||
|
if (optval_end - optval != PAGE_SIZE)
|
||||||
|
return 0; /* EPERM, unexpected data size */
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->level != SOL_CUSTOM)
|
if (ctx->level != SOL_CUSTOM)
|
||||||
return 0; /* EPERM, deny everything except custom level */
|
return 0; /* EPERM, deny everything except custom level */
|
||||||
|
|
||||||
|
@ -81,12 +107,14 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
||||||
__u8 *optval = ctx->optval;
|
__u8 *optval = ctx->optval;
|
||||||
struct sockopt_sk *storage;
|
struct sockopt_sk *storage;
|
||||||
|
|
||||||
if (ctx->level == SOL_IP && ctx->optname == IP_TOS)
|
if (ctx->level == SOL_IP && ctx->optname == IP_TOS) {
|
||||||
/* Not interested in SOL_IP:IP_TOS;
|
/* Not interested in SOL_IP:IP_TOS;
|
||||||
* let next BPF program in the cgroup chain or kernel
|
* let next BPF program in the cgroup chain or kernel
|
||||||
* handle it.
|
* handle it.
|
||||||
*/
|
*/
|
||||||
|
ctx->optlen = 0; /* bypass optval>PAGE_SIZE */
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
|
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
|
||||||
/* Overwrite SO_SNDBUF value */
|
/* Overwrite SO_SNDBUF value */
|
||||||
|
@ -112,6 +140,28 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
|
||||||
|
/* Original optlen is larger than PAGE_SIZE. */
|
||||||
|
if (ctx->optlen != PAGE_SIZE * 2)
|
||||||
|
return 0; /* EPERM, unexpected data size */
|
||||||
|
|
||||||
|
if (optval + 1 > optval_end)
|
||||||
|
return 0; /* EPERM, bounds check */
|
||||||
|
|
||||||
|
/* Make sure we can trim the buffer. */
|
||||||
|
optval[0] = 0;
|
||||||
|
ctx->optlen = 1;
|
||||||
|
|
||||||
|
/* Usepace buffer is PAGE_SIZE * 2, but BPF
|
||||||
|
* program can only see the first PAGE_SIZE
|
||||||
|
* bytes of data.
|
||||||
|
*/
|
||||||
|
if (optval_end - optval != PAGE_SIZE)
|
||||||
|
return 0; /* EPERM, unexpected data size */
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->level != SOL_CUSTOM)
|
if (ctx->level != SOL_CUSTOM)
|
||||||
return 0; /* EPERM, deny everything except custom level */
|
return 0; /* EPERM, deny everything except custom level */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue