Re: [PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status
Hi Feng, 2017-04-13 11:22 GMT+08:00 Gao Feng: [...] >> No, it's better to do this together, there are two invocations, it's not >> good to >> copy these codes twice. > > You mean " on &= ~ IPS_UNCHANGEABLE_MASK " and " off &= ~ > IPS_UNCHANGEABLE_MASK " seems duplicated? I see. I misunderstood your initial meaning. So I will send V2 :) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status
Hi Liping, > -Original Message- > From: Liping Zhang [mailto:zlpnob...@gmail.com] > Sent: Thursday, April 13, 2017 11:15 AM > To: Gao Feng <gfree.w...@foxmail.com> > Cc: Liping Zhang <zlpnob...@163.com>; Pablo Neira Ayuso > <pa...@netfilter.org>; Netfilter Developer Mailing List > <netfilter-devel@vger.kernel.org>; cerne...@chromium.org > Subject: Re: [PATCH nf] netfilter: ctnetlink: make it safer when updating > ct->status > > Hi Feng, > > 2017-04-13 10:42 GMT+08:00 Gao Feng <gfree.w...@foxmail.com>: > [...] > >> +static void > >> +__ctnetlink_change_status(struct nf_conn *ct, unsigned long on, > >> + unsigned long off) { > >> + unsigned long mask; > >> + unsigned int bit; > >> + > >> + for (bit = 0; bit < __IPS_MAX_BIT; bit++) { > >> + mask = 1 << bit; > >> + /* Ignore these unchangable bits */ > >> + if (mask & IPS_UNCHANGEABLE_MASK) > >> + continue; > > > > How about clear the bits of on and off with IPS_UNCHANGEABLE_MASK > > before loop. > > Like "on &= ~ IPS_UNCHANGEABLE_MASK"; > > Then the "if (mask & IPS_UNCHANGEABLE_MASK)" could be removed. > > No, it's better to do this together, there are two invocations, it's not good > to > copy these codes twice. You mean " on &= ~ IPS_UNCHANGEABLE_MASK " and " off &= ~ IPS_UNCHANGEABLE_MASK " seems duplicated? > > > > > BTW, when some bits are set both of on and off, the "on" would be > > effective, but "off" not. > > This won't happen, see the invocation: > 1. __ctnetlink_change_status(ct, status, 0); 2. __ctnetlink_change_status(ct, > status, ~status); > > > So I think we could use BUILD_BUG_ON to avoid it during building. > > BUILD_BUG_ON(on); > > Btw, this won't help, BUILD_BUG_ON is only effective on compile time, but > "on" and "off" will be modified at the running time. You are right. This new function would be used frequently at running time. Regards Feng -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status
Hi Feng, 2017-04-13 10:42 GMT+08:00 Gao Feng: [...] >> +static void >> +__ctnetlink_change_status(struct nf_conn *ct, unsigned long on, >> + unsigned long off) >> +{ >> + unsigned long mask; >> + unsigned int bit; >> + >> + for (bit = 0; bit < __IPS_MAX_BIT; bit++) { >> + mask = 1 << bit; >> + /* Ignore these unchangable bits */ >> + if (mask & IPS_UNCHANGEABLE_MASK) >> + continue; > > How about clear the bits of on and off with IPS_UNCHANGEABLE_MASK before > loop. > Like "on &= ~ IPS_UNCHANGEABLE_MASK"; > Then the "if (mask & IPS_UNCHANGEABLE_MASK)" could be removed. No, it's better to do this together, there are two invocations, it's not good to copy these codes twice. > > BTW, when some bits are set both of on and off, the "on" would be effective, > but "off" not. This won't happen, see the invocation: 1. __ctnetlink_change_status(ct, status, 0); 2. __ctnetlink_change_status(ct, status, ~status); > So I think we could use BUILD_BUG_ON to avoid it during building. > BUILD_BUG_ON(on); Btw, this won't help, BUILD_BUG_ON is only effective on compile time, but "on" and "off" will be modified at the running time. -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status
> -Original Message- > From: Gao Feng [mailto:gfree.w...@foxmail.com] > Sent: Thursday, April 13, 2017 10:42 AM > To: 'Liping Zhang' <zlpnob...@163.com>; 'pa...@netfilter.org' > <pa...@netfilter.org> > Cc: 'netfilter-devel@vger.kernel.org' <netfilter-devel@vger.kernel.org>; > 'cerne...@chromium.org' <cerne...@chromium.org>; 'Liping Zhang' > <zlpnob...@gmail.com> > Subject: RE: [PATCH nf] netfilter: ctnetlink: make it safer when updating > ct->status > > Hi Liping, > > > -Original Message- > > From: netfilter-devel-ow...@vger.kernel.org > > [mailto:netfilter-devel-ow...@vger.kernel.org] On Behalf Of Liping > > Zhang > > Sent: Wednesday, April 12, 2017 11:57 PM > > To: pa...@netfilter.org > > Cc: netfilter-devel@vger.kernel.org; cerne...@chromium.org; Liping > > Zhang <zlpnob...@gmail.com> > > Subject: [PATCH nf] netfilter: ctnetlink: make it safer when updating > > ct->status > > > > From: Liping Zhang <zlpnob...@gmail.com> > > > > User can update the ct->status via nfnetlink, but using a non-atomic > > operation "ct->status |= status;". This is unsafe, and may clear > > IPS_DYING_BIT bit set by another CPU unexpectedly. For example: > > CPU0CPU1 > > ctnetlink_change_status__nf_conntrack_find_get > > old = ct->status nf_ct_gc_expired > > - nf_ct_kill > > - test_and_set_bit(IPS_DYING_BIT > > - - > > ct->status = old | status;<-- oops, _DYING_ is cleared! > > > > So using a series of atomic bit operation to solve the above issue. > > > > Also note, user shouldn't set IPS_TEMPLATE, IPS_SEQ_ADJUST directly. > > > > If we set the IPS_TEMPLATE_BIT, ct will be freed by nf_ct_tmpl_free, > > but actually it is alloced by nf_conntrack_alloc. > > > > If we set the IPS_SEQ_ADJUST_BIT, this may cause the NULL pointer > > deference, as the nfct_seqadj(ct) maybe NULL. > > > > So make these two bits be unchangable too. > > > > Last, add some comments to describe the logic change due to the commit > > a963d710f367 ("netfilter: ctnetlink: Fix regression in CTA_STATUS > > processing"), which makes me feel a little confusing. > > > > Signed-off-by: Liping Zhang <zlpnob...@gmail.com> > > --- > > include/uapi/linux/netfilter/nf_conntrack_common.h | 13 +--- > > net/netfilter/nf_conntrack_netlink.c | 35 > > -- > > 2 files changed, 35 insertions(+), 13 deletions(-) > > > > diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h > > b/include/uapi/linux/netfilter/nf_conntrack_common.h > > index 6a8e33d..38fc383 100644 > > --- a/include/uapi/linux/netfilter/nf_conntrack_common.h > > +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h > > @@ -82,10 +82,6 @@ enum ip_conntrack_status { > > IPS_DYING_BIT = 9, > > IPS_DYING = (1 << IPS_DYING_BIT), > > > > - /* Bits that cannot be altered from userland. */ > > - IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | > > -IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING), > > - > > /* Connection has fixed timeout. */ > > IPS_FIXED_TIMEOUT_BIT = 10, > > IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), @@ -101,6 > > +97,15 @@ enum ip_conntrack_status { > > /* Conntrack got a helper explicitly attached via CT target. */ > > IPS_HELPER_BIT = 13, > > IPS_HELPER = (1 << IPS_HELPER_BIT), > > + > > + /* Be careful here, modifying these bits can make things messy, > > +* so don't let users modify them directly. > > +*/ > > + IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | > > +IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING | > > +IPS_SEQ_ADJUST | IPS_TEMPLATE), > > + > > + __IPS_MAX_BIT = 14, > > }; > > > > /* Connection tracking event types */ diff --git > > a/net/netfilter/nf_conntrack_netlink.c > > b/net/netfilter/nf_conntrack_netlink.c > > index 908d858..68efe1a 100644 > > --- a/net/netfilter/nf_conntrack_netlink.c > > +++ b/net/netfilter/nf_conntrack_netlink.c > > @@ -1419,6 +1419,26 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, > > } #endif > > > > +static void > > +__ctnetlink_change_status(struct nf_conn *ct, unsigned long on, > >
RE: [PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status
Hi Liping, > -Original Message- > From: netfilter-devel-ow...@vger.kernel.org > [mailto:netfilter-devel-ow...@vger.kernel.org] On Behalf Of Liping Zhang > Sent: Wednesday, April 12, 2017 11:57 PM > To: pa...@netfilter.org > Cc: netfilter-devel@vger.kernel.org; cerne...@chromium.org; Liping Zhang > <zlpnob...@gmail.com> > Subject: [PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status > > From: Liping Zhang <zlpnob...@gmail.com> > > User can update the ct->status via nfnetlink, but using a non-atomic operation > "ct->status |= status;". This is unsafe, and may clear IPS_DYING_BIT bit set by > another CPU unexpectedly. For example: > CPU0CPU1 > ctnetlink_change_status__nf_conntrack_find_get > old = ct->status nf_ct_gc_expired > - nf_ct_kill > - test_and_set_bit(IPS_DYING_BIT > - - > ct->status = old | status;<-- oops, _DYING_ is cleared! > > So using a series of atomic bit operation to solve the above issue. > > Also note, user shouldn't set IPS_TEMPLATE, IPS_SEQ_ADJUST directly. > > If we set the IPS_TEMPLATE_BIT, ct will be freed by nf_ct_tmpl_free, but > actually it is alloced by nf_conntrack_alloc. > > If we set the IPS_SEQ_ADJUST_BIT, this may cause the NULL pointer deference, > as the nfct_seqadj(ct) maybe NULL. > > So make these two bits be unchangable too. > > Last, add some comments to describe the logic change due to the commit > a963d710f367 ("netfilter: ctnetlink: Fix regression in CTA_STATUS processing"), > which makes me feel a little confusing. > > Signed-off-by: Liping Zhang <zlpnob...@gmail.com> > --- > include/uapi/linux/netfilter/nf_conntrack_common.h | 13 +--- > net/netfilter/nf_conntrack_netlink.c | 35 > -- > 2 files changed, 35 insertions(+), 13 deletions(-) > > diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h > b/include/uapi/linux/netfilter/nf_conntrack_common.h > index 6a8e33d..38fc383 100644 > --- a/include/uapi/linux/netfilter/nf_conntrack_common.h > +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h > @@ -82,10 +82,6 @@ enum ip_conntrack_status { > IPS_DYING_BIT = 9, > IPS_DYING = (1 << IPS_DYING_BIT), > > - /* Bits that cannot be altered from userland. */ > - IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | > - IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING), > - > /* Connection has fixed timeout. */ > IPS_FIXED_TIMEOUT_BIT = 10, > IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), @@ -101,6 > +97,15 @@ enum ip_conntrack_status { > /* Conntrack got a helper explicitly attached via CT target. */ > IPS_HELPER_BIT = 13, > IPS_HELPER = (1 << IPS_HELPER_BIT), > + > + /* Be careful here, modifying these bits can make things messy, > + * so don't let users modify them directly. > + */ > + IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | > + IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING | > + IPS_SEQ_ADJUST | IPS_TEMPLATE), > + > + __IPS_MAX_BIT = 14, > }; > > /* Connection tracking event types */ > diff --git a/net/netfilter/nf_conntrack_netlink.c > b/net/netfilter/nf_conntrack_netlink.c > index 908d858..68efe1a 100644 > --- a/net/netfilter/nf_conntrack_netlink.c > +++ b/net/netfilter/nf_conntrack_netlink.c > @@ -1419,6 +1419,26 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, } > #endif > > +static void > +__ctnetlink_change_status(struct nf_conn *ct, unsigned long on, > + unsigned long off) > +{ > + unsigned long mask; > + unsigned int bit; > + > + for (bit = 0; bit < __IPS_MAX_BIT; bit++) { > + mask = 1 << bit; > + /* Ignore these unchangable bits */ > + if (mask & IPS_UNCHANGEABLE_MASK) > + continue; How about clear the bits of on and off with IPS_UNCHANGEABLE_MASK before loop. Like "on &= ~ IPS_UNCHANGEABLE_MASK"; Then the "if (mask & IPS_UNCHANGEABLE_MASK)" could be removed. BTW, when some bits are set both of on and off, the "on" would be effective, but "off" not. So I think we could use BUILD_BUG_ON to avoid it during building. BUILD_BUG_ON(on); Best Regards Feng > + > + if (on & mask) > + set_bit(bit, >status); > + else if (off & mask) > +
[PATCH nf] netfilter: ctnetlink: make it safer when updating ct->status
From: Liping ZhangUser can update the ct->status via nfnetlink, but using a non-atomic operation "ct->status |= status;". This is unsafe, and may clear IPS_DYING_BIT bit set by another CPU unexpectedly. For example: CPU0CPU1 ctnetlink_change_status__nf_conntrack_find_get old = ct->status nf_ct_gc_expired - nf_ct_kill - test_and_set_bit(IPS_DYING_BIT - - ct->status = old | status;<-- oops, _DYING_ is cleared! So using a series of atomic bit operation to solve the above issue. Also note, user shouldn't set IPS_TEMPLATE, IPS_SEQ_ADJUST directly. If we set the IPS_TEMPLATE_BIT, ct will be freed by nf_ct_tmpl_free, but actually it is alloced by nf_conntrack_alloc. If we set the IPS_SEQ_ADJUST_BIT, this may cause the NULL pointer deference, as the nfct_seqadj(ct) maybe NULL. So make these two bits be unchangable too. Last, add some comments to describe the logic change due to the commit a963d710f367 ("netfilter: ctnetlink: Fix regression in CTA_STATUS processing"), which makes me feel a little confusing. Signed-off-by: Liping Zhang --- include/uapi/linux/netfilter/nf_conntrack_common.h | 13 +--- net/netfilter/nf_conntrack_netlink.c | 35 -- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 6a8e33d..38fc383 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -82,10 +82,6 @@ enum ip_conntrack_status { IPS_DYING_BIT = 9, IPS_DYING = (1 << IPS_DYING_BIT), - /* Bits that cannot be altered from userland. */ - IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | -IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING), - /* Connection has fixed timeout. */ IPS_FIXED_TIMEOUT_BIT = 10, IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), @@ -101,6 +97,15 @@ enum ip_conntrack_status { /* Conntrack got a helper explicitly attached via CT target. */ IPS_HELPER_BIT = 13, IPS_HELPER = (1 << IPS_HELPER_BIT), + + /* Be careful here, modifying these bits can make things messy, +* so don't let users modify them directly. +*/ + IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | +IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING | +IPS_SEQ_ADJUST | IPS_TEMPLATE), + + __IPS_MAX_BIT = 14, }; /* Connection tracking event types */ diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 908d858..68efe1a 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1419,6 +1419,26 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, } #endif +static void +__ctnetlink_change_status(struct nf_conn *ct, unsigned long on, + unsigned long off) +{ + unsigned long mask; + unsigned int bit; + + for (bit = 0; bit < __IPS_MAX_BIT; bit++) { + mask = 1 << bit; + /* Ignore these unchangable bits */ + if (mask & IPS_UNCHANGEABLE_MASK) + continue; + + if (on & mask) + set_bit(bit, >status); + else if (off & mask) + clear_bit(bit, >status); + } +} + static int ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[]) { @@ -1438,10 +1458,7 @@ ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[]) /* ASSURED bit can only be set */ return -EBUSY; - /* Be careful here, modifying NAT bits can screw up things, -* so don't let users modify them directly if they don't pass -* nf_nat_range. */ - ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK); + __ctnetlink_change_status(ct, status, 0); return 0; } @@ -1632,7 +1649,7 @@ ctnetlink_change_seq_adj(struct nf_conn *ct, if (ret < 0) return ret; - ct->status |= IPS_SEQ_ADJUST; + set_bit(IPS_SEQ_ADJUST_BIT, >status); } if (cda[CTA_SEQ_ADJ_REPLY]) { @@ -1641,7 +1658,7 @@ ctnetlink_change_seq_adj(struct nf_conn *ct, if (ret < 0) return ret; - ct->status |= IPS_SEQ_ADJUST; + set_bit(IPS_SEQ_ADJUST_BIT, >status); } return 0; @@ -2295,10 +2312,10 @@ ctnetlink_update_status(struct nf_conn *ct, const struct nlattr * const cda[]) /* This check is less strict than ctnetlink_change_status() *