On Thu, Jul 14, 2022 at 06:16:14AM +0300, Vitaliy Makkoveev wrote:
> > On 14 Jul 2022, at 05:51, Visa Hankala <[email protected]> wrote:
> > 
> > On Thu, Jul 14, 2022 at 04:39:33AM +0300, Vitaliy Makkoveev wrote:
> >> It looks like sometimes the `unp_conn' doesn't reloaded in
> >> the "unp->unp_conn != unp2" check, and READ_ONCE() should prevent this.
> > 
> > Are you sure about the non-reloading of unp->unp_conn? I did a quick
> > look in the compiled output on amd64, mips64 and riscv64, and did not
> > spot any obvious change (there is no change on mips64 and riscv64).
> > 
> 
> It seems to be. Otherwise I have no explanation of syzkaller reports.
> 
> May be it is not compiler, but CPU cache specific. I see the
> difference on amd64, compiler loads to register before compare.
> 
> mov    0x20(%r13),%rax
> cmp    %r12,%rax
> je     0xffff...
> 
> instead of
> 
> cmp    %r12,0x20(%r13)
> je     0xffff...
> 
> I can???t say what is the real difference in cmp behaviour in both
> cases. May be some one could explain.

For me this chunk of assembly looks equivalent.  If this is the
only binary change, I don't expect READ_ONCE() to fix syzkaller.
I am not against adding READ_ONCE(), it prevents unexpected compiler
optimisations.  But currently such a optimization does not happen.

> > The loads are surrounded by lock/reference operations that imply
> > memory clobbering. The compiler should not assume that unp->unp_conn
> > is unchanged.

Maybe I did not understand correctly why READ_ONCE() is needed.

> >>        if ((unp2 = unp->unp_conn) == NULL)
> >>                return NULL;
> >> 
> >>        so2 = unp2->unp_socket;

Can the compiler modify this code to this?

            if ((unp2 = unp->unp_conn) == NULL)
                    return NULL;
            so2 = unp->unp_conn->unp_socket;

Then we can get a NULL deref if another CPU sets unp->unp_conn to
NULL between our two reads.  There READ_ONCE() makes sense.

> >>                solock(so);
> >> 
> >>                /* Datagram socket could be reconnected due to re-lock. */
> >>                if (unp->unp_conn != unp2) {
> >>                        sounlock(so2);
> >>                        unp_rele(unp2);
> >>                        goto again;
> >>                }
> >> 
> >>                unp_rele(unp2);

Can the compiler assume unp->unp_conn == unp2 ?  Then it would be
valid to do unp_rele(unp->unp_conn).  Not sure whether C standard
allows this.  And it would be stupid if the compiler would do that.

Sould we use more READ_ONCE() to be safe?

bluhm

Reply via email to