The retval value is moved to struct bpf_cg_run_ctx for ease of access in different prog types with different context structs layouts. The helper implementation (to be added in a later patch in the series) can simply perform a container_of from current->bpf_ctx to retrieve bpf_cg_run_ctx. Unfortunately, there is no easy way to access the current task_struct via the verifier BPF bytecode rewrite, aside from possibly calling a helper, so a pointer to current task is added to struct bpf_sockopt_kern so that the rewritten BPF bytecode can access struct bpf_cg_run_ctx with an indirection. For backward compatibility, if a getsockopt program rejects a syscall by returning 0, an -EPERM will be generated, by having the BPF_PROG_RUN_ARRAY_CG family macros automatically set the retval to -EPERM. Unlike prior to this patch, this -EPERM will be visible to ctx->retval for any other hooks down the line in the prog array. Additionally, the restriction that getsockopt filters can only set the retval to 0 is removed, considering that certain getsockopt implementations may return optlen. Filters are now able to set the value arbitrarily.
Showing the changes to kernel (1 of 3 files). Show all
Unified · Split
| @@ -1079,7 +1079,7 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, · 1078 unmodified lines | ||
| 1079 | 1079 | cgrp->bpf.effective[atype], skb, __bpf_prog_run_save_cb); |
| 1080 | 1080 | } else { |
| 1081 | 1081 | ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], skb, |
| 1082 | − __bpf_prog_run_save_cb); | |
| 1082 | + __bpf_prog_run_save_cb, 0); | |
| 1083 | 1083 | } |
| 1084 | 1084 | bpf_restore_data_end(skb, saved_data_end); |
| 1085 | 1085 | __skb_pull(skb, offset); |
| @@ -1108,7 +1108,7 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk, · 22 unmodified lines | ||
| 1108 | 1108 | struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); |
| 1109 | 1109 | |
| 1110 | 1110 | return BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sk, |
| 1111 | − bpf_prog_run); | |
| 1111 | + bpf_prog_run, 0); | |
| 1112 | 1112 | } |
| 1113 | 1113 | EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); |
| 1114 | 1114 | |
| @@ -1154,7 +1154,7 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk, · 39 unmodified lines | ||
| 1154 | 1154 | |
| 1155 | 1155 | cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); |
| 1156 | 1156 | return BPF_PROG_RUN_ARRAY_CG_FLAGS(cgrp->bpf.effective[atype], &ctx, |
| 1157 | − bpf_prog_run, flags); | |
| 1157 | + bpf_prog_run, 0, flags); | |
| 1158 | 1158 | } |
| 1159 | 1159 | EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr); |
| 1160 | 1160 | |
| @@ -1181,7 +1181,7 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, · 20 unmodified lines | ||
| 1181 | 1181 | struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); |
| 1182 | 1182 | |
| 1183 | 1183 | return BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sock_ops, |
| 1184 | − bpf_prog_run); | |
| 1184 | + bpf_prog_run, 0); | |
| 1185 | 1185 | } |
| 1186 | 1186 | EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops); |
| 1187 | 1187 | |
| @@ -1199,7 +1199,7 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, · 11 unmodified lines | ||
| 1199 | 1199 | rcu_read_lock(); |
| 1200 | 1200 | cgrp = task_dfl_cgroup(current); |
| 1201 | 1201 | ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, |
| 1202 | − bpf_prog_run); | |
| 1202 | + bpf_prog_run, 0); | |
| 1203 | 1203 | rcu_read_unlock(); |
| 1204 | 1204 | |
| 1205 | 1205 | return ret; |
| @@ -1330,7 +1330,8 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, · 124 unmodified lines | ||
| 1330 | 1330 | |
| 1331 | 1331 | rcu_read_lock(); |
| 1332 | 1332 | cgrp = task_dfl_cgroup(current); |
| 1333 | − ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, bpf_prog_run); | |
| 1333 | + ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx, | |
| 1334 | + bpf_prog_run, 0); | |
| 1334 | 1335 | rcu_read_unlock(); |
| 1335 | 1336 | |
| 1336 | 1337 | kfree(ctx.cur_val); |
| @@ -1445,7 +1446,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, · 108 unmodified lines | ||
| 1445 | 1446 | |
| 1446 | 1447 | lock_sock(sk); |
| 1447 | 1448 | ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_SETSOCKOPT], |
| 1448 | − &ctx, bpf_prog_run); | |
| 1449 | + &ctx, bpf_prog_run, 0); | |
| 1449 | 1450 | release_sock(sk); |
| 1450 | 1451 | |
| 1451 | 1452 | if (ret) |
| @@ -1509,7 +1510,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, · 57 unmodified lines | ||
| 1509 | 1510 | .sk = sk, |
| 1510 | 1511 | .level = level, |
| 1511 | 1512 | .optname = optname, |
| 1512 | − .retval = retval, | |
| 1513 | + .current_task = current, | |
| 1513 | 1514 | }; |
| 1514 | 1515 | int ret; |
| 1515 | 1516 | |
| @@ -1553,10 +1554,10 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, · 37 unmodified lines | ||
| 1553 | 1554 | |
| 1554 | 1555 | lock_sock(sk); |
| 1555 | 1556 | ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_GETSOCKOPT], |
| 1556 | − &ctx, bpf_prog_run); | |
| 1557 | + &ctx, bpf_prog_run, retval); | |
| 1557 | 1558 | release_sock(sk); |
| 1558 | 1559 | |
| 1559 | − if (ret) | |
| 1560 | + if (ret < 0) | |
| 1560 | 1561 | goto out; |
| 1561 | 1562 | |
| 1562 | 1563 | if (ctx.optlen > max_optlen || ctx.optlen < 0) { |
| @@ -1564,14 +1565,6 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, · 1 unmodified line | ||
| 1564 | 1565 | goto out; |
| 1565 | 1566 | } |
| 1566 | 1567 | |
| 1567 | − /* BPF programs only allowed to set retval to 0, not some | |
| 1568 | − * arbitrary value. | |
| 1569 | − */ | |
| 1570 | − if (ctx.retval != 0 && ctx.retval != retval) { | |
| 1571 | − ret = -EFAULT; | |
| 1572 | − goto out; | |
| 1573 | − } | |
| 1574 | − | |
| 1575 | 1568 | if (ctx.optlen != 0) { |
| 1576 | 1569 | if (copy_to_user(optval, ctx.optval, ctx.optlen) || |
| 1577 | 1570 | put_user(ctx.optlen, optlen)) { |
| @@ -1580,8 +1573,6 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, · 2 unmodified lines | ||
| 1580 | 1573 | } |
| 1581 | 1574 | } |
| 1582 | 1575 | |
| 1583 | − ret = ctx.retval; | |
| 1584 | − | |
| 1585 | 1576 | out: |
| 1586 | 1577 | sockopt_free_buf(&ctx, &buf); |
| 1587 | 1578 | return ret; |
| @@ -1596,10 +1587,10 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level, · 8 unmodified lines | ||
| 1596 | 1587 | .sk = sk, |
| 1597 | 1588 | .level = level, |
| 1598 | 1589 | .optname = optname, |
| 1599 | − .retval = retval, | |
| 1600 | 1590 | .optlen = *optlen, |
| 1601 | 1591 | .optval = optval, |
| 1602 | 1592 | .optval_end = optval + *optlen, |
| 1593 | + .current_task = current, | |
| 1603 | 1594 | }; |
| 1604 | 1595 | int ret; |
| 1605 | 1596 | |
| @@ -1612,25 +1603,19 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level, · 6 unmodified lines | ||
| 1612 | 1603 | */ |
| 1613 | 1604 | |
| 1614 | 1605 | ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_GETSOCKOPT], |
| 1615 | − &ctx, bpf_prog_run); | |
| 1616 | − if (ret) | |
| 1606 | + &ctx, bpf_prog_run, retval); | |
| 1607 | + if (ret < 0) | |
| 1617 | 1608 | return ret; |
| 1618 | 1609 | |
| 1619 | 1610 | if (ctx.optlen > *optlen) |
| 1620 | 1611 | return -EFAULT; |
| 1621 | 1612 | |
| 1622 | − /* BPF programs only allowed to set retval to 0, not some | |
| 1623 | − * arbitrary value. | |
| 1624 | − */ | |
| 1625 | − if (ctx.retval != 0 && ctx.retval != retval) | |
| 1626 | − return -EFAULT; | |
| 1627 | − | |
| 1628 | 1613 | /* BPF programs can shrink the buffer, export the modifications. |
| 1629 | 1614 | */ |
| 1630 | 1615 | if (ctx.optlen != 0) |
| 1631 | 1616 | *optlen = ctx.optlen; |
| 1632 | 1617 | |
| 1633 | − return ctx.retval; | |
| 1618 | + return ret; | |
| 1634 | 1619 | } |
| 1635 | 1620 | #endif |
| 1636 | 1621 | |
| @@ -2046,10 +2031,39 @@ static u32 cg_sockopt_convert_ctx_access(enum bpf_access_type type, · 409 unmodified lines | ||
| 2046 | 2031 | *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optlen); |
| 2047 | 2032 | break; |
| 2048 | 2033 | case offsetof(struct bpf_sockopt, retval): |
| 2049 | − if (type == BPF_WRITE) | |
| 2050 | − *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, retval); | |
| 2051 | − else | |
| 2052 | − *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, retval); | |
| 2034 | + BUILD_BUG_ON(offsetof(struct bpf_cg_run_ctx, run_ctx) != 0); | |
| 2035 | + | |
| 2036 | + if (type == BPF_WRITE) { | |
| 2037 | + int treg = BPF_REG_9; | |
| 2038 | + | |
| 2039 | + if (si->src_reg == treg || si->dst_reg == treg) | |
| 2040 | + --treg; | |
| 2041 | + if (si->src_reg == treg || si->dst_reg == treg) | |
| 2042 | + --treg; | |
| 2043 | + *insn++ = BPF_STX_MEM(BPF_DW, si->dst_reg, treg, | |
| 2044 | + offsetof(struct bpf_sockopt_kern, tmp_reg)); | |
| 2045 | + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_sockopt_kern, current_task), | |
| 2046 | + treg, si->dst_reg, | |
| 2047 | + offsetof(struct bpf_sockopt_kern, current_task)); | |
| 2048 | + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct task_struct, bpf_ctx), | |
| 2049 | + treg, treg, | |
| 2050 | + offsetof(struct task_struct, bpf_ctx)); | |
| 2051 | + *insn++ = BPF_STX_MEM(BPF_FIELD_SIZEOF(struct bpf_cg_run_ctx, retval), | |
| 2052 | + treg, si->src_reg, | |
| 2053 | + offsetof(struct bpf_cg_run_ctx, retval)); | |
| 2054 | + *insn++ = BPF_LDX_MEM(BPF_DW, treg, si->dst_reg, | |
| 2055 | + offsetof(struct bpf_sockopt_kern, tmp_reg)); | |
| 2056 | + } else { | |
| 2057 | + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_sockopt_kern, current_task), | |
| 2058 | + si->dst_reg, si->src_reg, | |
| 2059 | + offsetof(struct bpf_sockopt_kern, current_task)); | |
| 2060 | + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct task_struct, bpf_ctx), | |
| 2061 | + si->dst_reg, si->dst_reg, | |
| 2062 | + offsetof(struct task_struct, bpf_ctx)); | |
| 2063 | + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_cg_run_ctx, retval), | |
| 2064 | + si->dst_reg, si->dst_reg, | |
| 2065 | + offsetof(struct bpf_cg_run_ctx, retval)); | |
| 2066 | + } | |
| 2053 | 2067 | break; |
| 2054 | 2068 | case offsetof(struct bpf_sockopt, optval): |
| 2055 | 2069 | *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optval); |