On 6/27/17, 3:00 PM, "Joe Stringer" <[email protected]> wrote:

    On 27 June 2017 at 13:40, Darrell Ball <[email protected]> wrote:
    >
    >
    > On 6/27/17, 1:20 PM, "Joe Stringer" <[email protected]> wrote:
    >
    >     On 27 June 2017 at 11:19, Darrell Ball <[email protected]> wrote:
    >     >
    >     >
    >     > On 6/27/17, 11:04 AM, "Joe Stringer" <[email protected]> wrote:
    >     >
    >     >     On 27 June 2017 at 10:51, Darrell Ball <[email protected]> wrote:
    >     >     >
    >     >     >
    >     >     > On 6/27/17, 10:47 AM, "Joe Stringer" <[email protected]> wrote:
    >     >     >
    >     >     >     On 27 June 2017 at 10:42, Darrell Ball <[email protected]> 
wrote:
    >     >     >     >
    >     >     >     >
    >     >     >     > On 6/27/17, 10:40 AM, "Joe Stringer" <[email protected]> 
wrote:
    >     >     >     >
    >     >     >     >     On 26 June 2017 at 18:19, Darrell Ball 
<[email protected]> wrote:
    >     >     >     >     >
    >     >     >     >     >
    >     >     >     >     > On 6/26/17, 4:49 PM, "Joe Stringer" 
<[email protected]> wrote:
    >     >     >     >     >
    >     >     >     >     >     On 26 June 2017 at 13:22, Joe Stringer 
<[email protected]> wrote:
    >     >     >     >     >     > On 26 June 2017 at 11:45, Darrell Ball 
<[email protected]> wrote:
    >     >     >     >     >     >> On 6/26/17, 10:22 AM, "Joe Stringer" 
<[email protected]> wrote:
    >     >     >     >     >     >>     On 23 June 2017 at 18:57, Darrell Ball 
<[email protected]> wrote:
    >     >     >     >     >     >>     > On 6/23/17, 4:08 PM, 
"[email protected] on behalf of Joe Stringer" 
<[email protected] on behalf of [email protected]> wrote:
    >     >     >     >     >     >>     >     On 17 June 2017 at 15:53, 
Darrell Ball <[email protected]> wrote:
    >     >     >     >     >     >>     >     > @@ -554,34 +681,50 @@ 
conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
    >     >     >     >     >     >>     >     >          nc->rev_key = nc->key;
    >     >     >     >     >     >>     >     >          
conn_key_reverse(&nc->rev_key);
    >     >     >     >     >     >>     >     >
    >     >     >     >     >     >>     >     > +        if (helper) {
    >     >     >     >     >     >>     >     > +            nc->alg = 
xstrdup(helper);
    >     >     >     >     >     >>     >     > +        }
    >     >     >     >     >     >>     >     > +
    >     >     >     >     >     >>     >     > +        if (alg_exp) {
    >     >     >     >     >     >>     >     > +            nc->alg_related = 
true;
    >     >     >     >     >     >>     >     > +            nc->mark = 
alg_exp->master_mark;
    >     >     >     >     >     >>     >     > +            nc->label = 
alg_exp->master_label;
    >     >     >     >     >     >>     >     > +            nc->master_key = 
alg_exp->master_key;
    >     >     >     >     >     >>     >     > +        }
    >     >     >     >     >     >>     >     > +
    >     >     >     >     >     >>     >     >          if (nat_action_info) {
    >     >     >     >     >     >>     >     >              nc->nat_info = 
xmemdup(nat_action_info, sizeof *nc->nat_info);
    >     >     >     >     >     >>     >     > -            
ct_rwlock_wrlock(&ct->nat_resources_lock);
    >     >     >     >     >     >>     >     > -
    >     >     >     >     >     >>     >     > -            bool nat_res = 
nat_select_range_tuple(ct, nc,
    >     >     >     >     >     >>     >     > -                              
                    conn_for_un_nat_copy);
    >     >     >     >     >     >>     >     >
    >     >     >     >     >     >>     >     > -            if (!nat_res) {
    >     >     >     >     >     >>     >     > -                
free(nc->nat_info);
    >     >     >     >     >     >>     >     > -                nc->nat_info 
= NULL;
    >     >     >     >     >     >>     >     > -                free (nc);
    >     >     >     >     >     >>     >     > -                
ct_rwlock_unlock(&ct->nat_resources_lock);
    >     >     >     >     >     >>     >     > -                return NULL;
    >     >     >     >     >     >>     >     > -            }
    >     >     >     >     >     >>     >     > +            if (alg_exp) {
    >     >     >     >     >     >>     >     > +                
nc->rev_key.src.addr = alg_nat_repl_addr;
    >     >     >     >     >     >>     >     > +                
nc->nat_info->nat_action = NAT_ACTION_DST;
    >     >     >     >     >     >>     >     > +                
*conn_for_un_nat_copy = *nc;
    >     >     >     >     >     >>     >     > +            } else {
    >     >     >     >     >     >>     >     > +                
ct_rwlock_wrlock(&ct->resources_lock);
    >     >     >     >     >     >>     >     > +                bool nat_res 
= nat_select_range_tuple(
    >     >     >     >     >     >>     >     > +                              
     ct, nc, conn_for_un_nat_copy);
    >     >     >     >     >     >>     >     > +
    >     >     >     >     >     >>     >     > +                if (!nat_res) 
{
    >     >     >     >     >     >>     >     > +                    
free(nc->nat_info);
    >     >     >     >     >     >>     >     > +                    
nc->nat_info = NULL;
    >     >     >     >     >     >>     >     > +                    free (nc);
    >     >     >     >     >     >>     >
    >     >     >     >     >     >>     >     I think that nc->alg may be 
leaked here? any reason it doesn't use
    >     >     >     >     >     >>     >     delete_conn()?
    >     >     >     >     >     >>     >
    >     >     >     >     >     >>     > Good
    >     >     >     >     >     >>     > Yes, alg will leak in this rare 
error case and yes, delete_conn() should be used
    >     >     >     >     >     >>     > here, as everywhere.
    >     >     >     >     >     >>
    >     >     >     >     >     >>     OK.
    >     >     >     >     >     >>
    >     >     >     >     >     >>     >     > +                    
ct_rwlock_unlock(&ct->resources_lock);
    >     >     >     >     >     >>     >     > +                    return 
NULL;
    >     >     >     >     >     >>     >     > +                }
    >     >     >     >     >     >>     >     >
    >     >     >     >     >     >>     >     > -            if 
(conn_for_un_nat_copy &&
    >     >     >     >     >     >>     >     > -                nc->conn_type 
== CT_CONN_TYPE_DEFAULT) {
    >     >     >     >     >     >>     >     >                  *nc = 
*conn_for_un_nat_copy;
    >     >     >     >     >     >>     >
    >     >     >     >     >     >>     >     Perhaps nc->alg and/or 
nc->nat_info may be leaked here?
    >     >     >     >     >     >>     >
    >     >     >     >     >     >>     > No, the un_nat conn has no such 
allocations, so there is nothing to leak.
    >     >     >     >     >     >>
    >     >     >     >     >     >>     I don't mean conn_for_un_nat_copy, I 
mean *nc which could have had an
    >     >     >     >     >     >>     xstrdup()'d 'alg' attached. Won't this 
overwrite all fields in 'nc'?
    >     >     >     >     >     >>
    >     >     >     >     >     >> I see your question now.
    >     >     >     >     >     >> No, at this point, the copy gets the same 
pointers to the alg string and nat_info.
    >     >     >     >     >     >> Only nc needs them and the un_nat copy 
ptrs are nulled.
    >     >     >     >     >     >> There is only one allocation set.
    >     >     >     >     >     >
    >     >     >     >     >     > Hmm. Maybe I'm just missing something, let 
me walk through it step by
    >     >     >     >     >     > step below and let's see where it goes.
    >     >     >     >     >     >
    >     >     >     >     >     >        if (helper) {
    >     >     >     >     >     >            nc->alg = xstrdup(helper);
    >     >     >     >     >     > ^ nc->alg is set
    >     >     >     >     >     >        }
    >     >     >     >     >     >
    >     >     >     >     >     >        if (alg_exp) {
    >     >     >     >     >     > ^ false; do not execute this block
    >     >     >     >     >     >            nc->alg_related = true;
    >     >     >     >     >     >            nc->mark = alg_exp->master_mark;
    >     >     >     >     >     >            nc->label = 
alg_exp->master_label;
    >     >     >     >     >     >            nc->master_key = 
alg_exp->master_key;
    >     >     >     >     >     >        }
    >     >     >     >     >     >
    >     >     >     >     >     >        if (nat_action_info) {
    >     >     >     >     >     > ^ true, execute this part
    >     >     >     >     >     >            nc->nat_info = 
xmemdup(nat_action_info, sizeof *nc->nat_info);
    >     >     >     >     >     >
    >     >     >     >     >     >            if (alg_exp) {
    >     >     >     >     >     > ^ false; skip to else
    >     >     >     >     >     >                nc->rev_key.src.addr = 
alg_nat_repl_addr;
    >     >     >     >     >     >                nc->nat_info->nat_action = 
NAT_ACTION_DST;
    >     >     >     >     >     >                *conn_for_un_nat_copy = *nc;
    >     >     >     >     >     >            } else {
    >     >     >     >     >     > ^ We go through this condition
    >     >     >     >     >     >                
ct_rwlock_wrlock(&ct->resources_lock);
    >     >     >     >     >     >                bool nat_res = 
nat_select_range_tuple(
    >     >     >     >     >     >                                   ct, nc, 
conn_for_un_nat_copy);
    >     >     >     >     >     >
    >     >     >     >     >     >                if (!nat_res) {
    >     >     >     >     >     > ^ false; do not execute this block
    >     >     >     >     >     >                    free(nc->nat_info);
    >     >     >     >     >     >                    nc->nat_info = NULL;
    >     >     >     >     >     >                    free (nc);
    >     >     >     >     >     >                    
ct_rwlock_unlock(&ct->resources_lock);
    >     >     >     >     >     >                    return NULL;
    >     >     >     >     >     >                }
    >     >     >     >     >     >
    >     >     >     >     >     >                *nc = *conn_for_un_nat_copy;
    >     >     >     >     >     > ^ Now:
    >     >     >     >     >     > nc->alg is overwritten by 
conn_for_un_nat_copy->alg
    >     >     >     >     >     > nc->nat_info is overwritten by 
conn_for_un_nat_copy->nat_info
    >     >     >     >     >     >
    >     >     >     >     >     > We don't free either of these.
    >     >     >     >     >
    >     >     >     >     >     As discussed offline, the copy of '*nc' into 
'*conn_for_un_nat_copy'
    >     >     >     >     >     nested inside nat_select_range_tuple() is 
very well hidden. This means
    >     >     >     >     >     that the above is not a problem... but what 
if (!nat_res) ? Then
    >     >     >     >     >     conn_for_un_nat_copy() has a reference to 
these alg/nat_info
    >     >     >     >     >     parameters which are freed from 'nc' inside 
that block, then
    >     >     >     >     >     'conn_for_un_nat_copy' is returned. Could 
there be a use-after-free
    >     >     >     >     >     then?
    >     >     >     >     >
    >     >     >     >     > Nope, because there is no un_nat conn.
    >     >     >     >
    >     >     >     >     So you mean that dangling references are returned 
inside
    >     >     >     >     conn_for_un_nat_copy but they're just not used?
    >     >     >     >
    >     >     >     > JTBC, nothing can be used, since there is not even a 
connection.
    >     >     >
    >     >     >     So if nat_select_range_tuple() is called, and it runs the 
"*nat_conn =
    >     >     >     *conn;" line which updates conn_for_un_nat_copy, then
    >     >     >     nat_select_range_tuple() fails to select a tuple and 
returns false,
    >     >     >     then the if (!nat_res) cleanup / return NULL path is 
called, then what
    >     >     >     cleans up the dangling pointers that are now in 
conn_for_un_nat_copy?
    >     >     >
    >     >     > conn_for_un_nat_copy is a local variable in the caller
    >     >
    >     >     Which is then potentially passed to another function,
    >     >     create_un_nat_conn(), which then does an xmemdup() to propagate 
those
    >     >     dangling pointers further.. seems like it would be safest to 
clear out
    >     >     the conn_for_un_nat_copy->alg and ->nat_info pointers before 
freeing
    >     >     'nc' in that path.
    >     >
    >     > No, as I stated already, since the un_nat creation (and also conn) 
never completed
    >     > the conntype is not set and create_un_nat_conn() is not called.
    >
    >     OK, now I see what you mean.
    >
    >     I think that clearing these pointers in that error path would make the
    >     code more explicit about the status of one of the parameters modified
    >     by this function, and would limit side-effects. I don't see a negative
    >     to this proposed change.
    >
    > Adding redundant clearing of pointers in the error path will not hurt
    > performance, so there is no problem there.
    
    Right.
    
    > It feels a little weird to clear a couple pointers in a case where the 
whole connection
    > context is dead.  It feels like painting your house while it is burning 
down.
    > Maybe a comment would serve the same purpose.
    
    Comments could help to some degree, but I think that my core concerns
    could be addressed with improvements to the code:
    * nat_select_range_tuple() sounds like it will select a range. Not
    only does it select the range, but it does full initialization of
    conn_for_un_nat_copy as well (via shallow-copy)
    * conn_not_found() allocates memory, then nat_select_range_tuple()
    shares pointers to that memory around, and yet the pointers don't
    share fate when conn_not_found() frees them. This makes it difficult
    to read the code and balance alloc/free.

I looked and noticed that the conn copy in nat_select_range_tuple() was never
needed there; I moved it to conn_not_found(). I agree it was not clear.

    * nc seems to be the owner of these memory allocations, but since
    they're propagated into conn_for_un_nat_copy, it's less clear which
    conn is supposed to own it.
    * The condition where these dangling pointers are shared up to the
    caller is not the exact same condition check in the original caller
    which prevents usage of those dangling pointers.

In the error case, I added a memset zero (with comment) of the
conn_for_un_nat_copy structure to document that the structure will
not be further used.

In the non-error case, those pointers are presently explicitly nulled.
conn_for_un_nat_copy->nat_info = NULL;
conn_for_un_nat_copy->alg = NULL;
I think this makes it clear that conn_for_un_nat_copy is not the owner 
of these pointers.

    A bunch of the above concerns aren't specific to this patch, they
    already exist in the upstream codebase and I'm probably just noticing
    because it's really new code and it's my first real dive into this
    area. But I do think there are improvements to be made in limiting the
    scope of potential changes from each function, and localizing things
    like initialization, memory alloc/free/ownership, etc. into ideally
    the same function.
    

















_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to