lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250810182506.GL222315@ZenIV>
Date: Sun, 10 Aug 2025 19:25:06 +0100
From: Al Viro <viro@...iv.linux.org.uk>
To: Ujwal Kundur <ujwal.kundur@...il.com>
Cc: allison.henderson@...cle.com, davem@...emloft.net, edumazet@...gle.com,
	kuba@...nel.org, pabeni@...hat.com, horms@...nel.org,
	netdev@...r.kernel.org, linux-rdma@...r.kernel.org,
	rds-devel@....oracle.com, linux-kernel@...r.kernel.org
Subject: Re: [PATCH net] rds: Fix endian annotations across various
 assignments

On Sun, Aug 10, 2025 at 06:47:05PM +0100, Al Viro wrote:

> >  		switch (type) {
> >  		case RDS_EXTHDR_NPATHS:
> >  			conn->c_npaths = min_t(int, RDS_MPATH_WORKERS,
> > -					       be16_to_cpu(buffer.rds_npaths));
> > +					      (__force __u16)buffer.rds_npaths);
> 
> No.  It will break on little-endian.  That's over-the-wire data you are
> dealing with; it's *NOT* going to be host-endian.  Fix the buggered
> annotations instead.

PS: be16_to_cpu() is not the same thing as a cast.  On a big-endian box,
a 16bit number 1000 (0x3e8) is stored as {3, 0xe8}; on a little-endian it's
{3, 0xe8} instead; {0xe8, 3} means 59395 there (0xe8 * 256 + 3).

be16_to_cpu() is a no-op on big-endian; on little-endian it converts the
big-endian 16bit to host-endian (internally it's a byteswap).

If over-the-wire data is stored in big-endian order (bits 8..15 in the
first byte, bits 0..7 in the second one) and you want to do any kind
of arithmetics with the value you've received, you can't blindly treat
it as u16 (unsigned short) field of structure - on big-endian boxen
it would work, but on little-endian it would give the wrong values
(59395 when sender had meant 1000).  be16_to_cpu() adjusts for that.

In the above, you have ->c_npaths definitely used as a number - this
net/rds/connection.c:924:       } while (++i < conn->c_npaths);
alone is enough to be convincing.  All other uses are of the same
sort - it's used in comparisons, so places using it are expecting
host-endian integer.

Assignment you've modified sets it to lesser of RDS_MPATH_WORKERS (8,
apparently) and the value of buffer.rds_npaths decoded as big-endian.

"buffer" is declared as
        union {
		struct rds_ext_header_version version;
		u16 rds_npaths;
		u32 rds_gen_num;
	} buffer;
and the value in it comes from
                type = rds_message_next_extension(hdr, &pos, &buffer, &len);
Then we look at the return value of that rds_message_next_extension() thing
and if it's RDS_EXTHDR_NPATHS we hit that branch.

That smells like parsing the incoming package, doesn't it?  A look at
rds_message_next_extension() seems to confirm that - we fetch the next
byte (that's our return value), then pick the corresponding size from
rds_exthdr_size[that_byte] and copy that many following bytes.

That code clearly expects the data to be stored in big-endian order -
it is not entirely impossible that somehow it gets host-endian (in
which case be16_to_cpu() would be a bug), but that's very unlikely.

*IF* that's over-the-wire data, the code is actually correct and the
problem is with wrong annotations -
        union {
		struct rds_ext_header_version version;
		__be16 rds_npaths;
		__be32 rds_gen_num;
	} buffer;
to reflect the actual data layout to be found in there.  Probably
static unsigned int     rds_exthdr_size[__RDS_EXTHDR_MAX] = {
[RDS_EXTHDR_NONE]       = 0,
[RDS_EXTHDR_VERSION]    = sizeof(struct rds_ext_header_version),
[RDS_EXTHDR_RDMA]       = sizeof(struct rds_ext_header_rdma),
[RDS_EXTHDR_RDMA_DEST]  = sizeof(struct rds_ext_header_rdma_dest),
[RDS_EXTHDR_NPATHS]     = sizeof(__be16),
[RDS_EXTHDR_GEN_NUM]    = sizeof(__be32),
};
for consistency sake.  Note that e.g. struct rds_ext_header_rdma_dest
is
struct rds_ext_header_rdma_dest {
        __be32                  h_rdma_rkey;
	__be32                  h_rdma_offset;
};
which also points towards "protocol data, fixed-endian"...

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ