Re: [PATCH nf-next 2/3] netfilter: nf_tables: validate register loads never access unitialised registers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Florian,

On Fri, May 05, 2023 at 01:16:55PM +0200, Florian Westphal wrote:
> Reject rules where a load occurs from a register that has not seen a
> store early in the same rule.
> 
> commit 4c905f6740a3 ("netfilter: nf_tables: initialize registers in nft_do_chain()")
> had to add a unconditional memset to the nftables register space to
> avoid leaking stack information to userspace.
> 
> This memset shows up in benchmarks.  After this change, this commit
> can be reverted again.
> 
> Note that this breaks userspace compatibility, because theoretically
> you can do
> 
>     rule 1: reg2 := meta load iif, reg2  == 1 jump ...
>     rule 2: reg2 == 2 jump ...   // read access with no store in this rule
> 
> ... after this change this is rejected.

We can probably achieve the same effect by recovering the runtime
register tracking patches I posted. It should be possible to add
unlikely() to the branch that checks for uninitialized data in source
register, that is missing in this patch:

https://patchwork.ozlabs.org/project/netfilter-devel/patch/20230505153130.2385-6-pablo@xxxxxxxxxxxxx/

such patch also equires these patches to add the helpers to load and
store:

https://patchwork.ozlabs.org/project/netfilter-devel/patch/20230505153130.2385-3-pablo@xxxxxxxxxxxxx/
https://patchwork.ozlabs.org/project/netfilter-devel/patch/20230505153130.2385-4-pablo@xxxxxxxxxxxxx/
https://patchwork.ozlabs.org/project/netfilter-devel/patch/20230505153130.2385-5-pablo@xxxxxxxxxxxxx/

I think those helpers to load and store on registers should have been
there since the beginning instead of opencoding operations on the
registers. I am going to collect some numbers with these patches
including unlikely() to the _load() checks. I fear I might have
introduced some subtle bug, I remember these patches are passing
selftests fine, but I am not sure we have enough of these selftests.

As you suggested, I also considered using the new track infrastructure
(the one I posted to achieve the combo expressions) to detect a read
to uninitialized registers from control plane, but it gets complicated
again because:

1) what level should register tracking happen at? rule, chain or from
   basechain to leaf chains (to ensure that we retain the freedom to
   make more transformation from userspace, eg. static flag for ruleset
   that never change to omit redundant operations in the generated
   bytecode). Your patch selects rule level. Chain level will lose
   context when jumping/going to another chain. Inspecting from
   basechain to leaf chains will be expensive in dynamic rulesets.

2) combo expressions omit the register store operation, the tracking
   infrastructure would need to distinguish between two situations:
   register data has been omitted or register data is missing because
   userspace provides bytecode that tries to read uninitialized
   registers.

While I agree control plane is ideal for this, because it allows to
reject a ruleset that reads on uninitialized register data, checking
at rule/chain level cripples expressiveness in a way that it will not
be easy to revert in the future if we want to change direction.

> Neither nftables nor iptables-nft generate such rules, each rule is
> always standalone.

That is true these days indeed. I like your approach because it is
simple. But my concern is that this limits expressiveness.

Thanks.



[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux