* [bug report] nfsd: Implement large extent array support in pNFS
@ 2025-07-16 19:40 Dan Carpenter
2025-07-16 20:31 ` Chuck Lever
0 siblings, 1 reply; 3+ messages in thread
From: Dan Carpenter @ 2025-07-16 19:40 UTC (permalink / raw)
To: Sergey Bashirov; +Cc: linux-nfs
Hello Sergey Bashirov,
Commit 067b594beb16 ("nfsd: Implement large extent array support in
pNFS") from Jun 21, 2025 (linux-next), leads to the following Smatch
static checker warning:
fs/nfsd/blocklayoutxdr.c:160 nfsd4_block_decode_layoutupdate()
warn: error code type promoted to positive: 'ret'
fs/nfsd/blocklayoutxdr.c
135 nfsd4_block_decode_layoutupdate(struct xdr_stream *xdr, struct iomap **iomapp,
136 int *nr_iomapsp, u32 block_size)
137 {
138 struct iomap *iomaps;
139 u32 nr_iomaps, expected, len, i;
140 __be32 nfserr;
141
142 if (xdr_stream_decode_u32(xdr, &nr_iomaps))
143 return nfserr_bad_xdr;
144
145 len = sizeof(__be32) + xdr_stream_remaining(xdr);
146 expected = sizeof(__be32) + nr_iomaps * PNFS_BLOCK_EXTENT_SIZE;
147 if (len != expected)
148 return nfserr_bad_xdr;
149
150 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
151 if (!iomaps)
152 return nfserr_delay;
153
154 for (i = 0; i < nr_iomaps; i++) {
155 struct pnfs_block_extent bex;
156 ssize_t ret;
157
158 ret = xdr_stream_decode_opaque_fixed(xdr,
159 &bex.vol_id, sizeof(bex.vol_id));
--> 160 if (ret < sizeof(bex.vol_id)) {
xdr_stream_decode_opaque_fixed() returns negative error codes or
sizeof(bex.vol_id) on success. If it returns a negative then the
condition is type promoted to size_t and the negative becomes a high
positive value and is treated as success.
There are so many ways to fix this bug.
#1: if (ret < 0 || ret < sizeof(bex.vol_id)) {
I love this approach but every other person in the world seems to hate
it.
#2: if (ret < (int)sizeof(bex.vol_id)) {
Fine. I don't love casts, but fine.
#3: if (ret != sizeof(bex.vol_id)) {
I like this well enough.
#4: Change xdr_stream_decode_opaque_fixed() to return zero on success.
This is the best fix.
regards,
dan carpenter
161 nfserr = nfserr_bad_xdr;
162 goto fail;
163 }
164
165 if (xdr_stream_decode_u64(xdr, &bex.foff)) {
166 nfserr = nfserr_bad_xdr;
167 goto fail;
168 }
169 if (bex.foff & (block_size - 1)) {
170 nfserr = nfserr_inval;
171 goto fail;
172 }
173
174 if (xdr_stream_decode_u64(xdr, &bex.len)) {
175 nfserr = nfserr_bad_xdr;
176 goto fail;
177 }
178 if (bex.len & (block_size - 1)) {
179 nfserr = nfserr_inval;
180 goto fail;
181 }
182
183 if (xdr_stream_decode_u64(xdr, &bex.soff)) {
184 nfserr = nfserr_bad_xdr;
185 goto fail;
186 }
187 if (bex.soff & (block_size - 1)) {
188 nfserr = nfserr_inval;
189 goto fail;
190 }
191
192 if (xdr_stream_decode_u32(xdr, &bex.es)) {
193 nfserr = nfserr_bad_xdr;
194 goto fail;
195 }
196 if (bex.es != PNFS_BLOCK_READWRITE_DATA) {
197 nfserr = nfserr_inval;
198 goto fail;
199 }
200
201 iomaps[i].offset = bex.foff;
202 iomaps[i].length = bex.len;
203 }
204
205 *iomapp = iomaps;
206 *nr_iomapsp = nr_iomaps;
207 return nfs_ok;
208 fail:
209 kfree(iomaps);
210 return nfserr;
211 }
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [bug report] nfsd: Implement large extent array support in pNFS
2025-07-16 19:40 [bug report] nfsd: Implement large extent array support in pNFS Dan Carpenter
@ 2025-07-16 20:31 ` Chuck Lever
2025-07-17 9:51 ` Sergey Bashirov
0 siblings, 1 reply; 3+ messages in thread
From: Chuck Lever @ 2025-07-16 20:31 UTC (permalink / raw)
To: Dan Carpenter, Sergey Bashirov; +Cc: linux-nfs
On 7/16/25 3:40 PM, Dan Carpenter wrote:
> Hello Sergey Bashirov,
>
> Commit 067b594beb16 ("nfsd: Implement large extent array support in
> pNFS") from Jun 21, 2025 (linux-next), leads to the following Smatch
> static checker warning:
>
> fs/nfsd/blocklayoutxdr.c:160 nfsd4_block_decode_layoutupdate()
> warn: error code type promoted to positive: 'ret'
>
> fs/nfsd/blocklayoutxdr.c
> 135 nfsd4_block_decode_layoutupdate(struct xdr_stream *xdr, struct iomap **iomapp,
> 136 int *nr_iomapsp, u32 block_size)
> 137 {
> 138 struct iomap *iomaps;
> 139 u32 nr_iomaps, expected, len, i;
> 140 __be32 nfserr;
> 141
> 142 if (xdr_stream_decode_u32(xdr, &nr_iomaps))
> 143 return nfserr_bad_xdr;
> 144
> 145 len = sizeof(__be32) + xdr_stream_remaining(xdr);
> 146 expected = sizeof(__be32) + nr_iomaps * PNFS_BLOCK_EXTENT_SIZE;
> 147 if (len != expected)
> 148 return nfserr_bad_xdr;
> 149
> 150 iomaps = kcalloc(nr_iomaps, sizeof(*iomaps), GFP_KERNEL);
> 151 if (!iomaps)
> 152 return nfserr_delay;
> 153
> 154 for (i = 0; i < nr_iomaps; i++) {
> 155 struct pnfs_block_extent bex;
> 156 ssize_t ret;
> 157
> 158 ret = xdr_stream_decode_opaque_fixed(xdr,
> 159 &bex.vol_id, sizeof(bex.vol_id));
> --> 160 if (ret < sizeof(bex.vol_id)) {
>
> xdr_stream_decode_opaque_fixed() returns negative error codes or
> sizeof(bex.vol_id) on success. If it returns a negative then the
> condition is type promoted to size_t and the negative becomes a high
> positive value and is treated as success.
>
> There are so many ways to fix this bug.
>
> #1: if (ret < 0 || ret < sizeof(bex.vol_id)) {
> I love this approach but every other person in the world seems to hate
> it.
>
> #2: if (ret < (int)sizeof(bex.vol_id)) {
> Fine. I don't love casts, but fine.
>
> #3: if (ret != sizeof(bex.vol_id)) {
> I like this well enough.
>
> #4: Change xdr_stream_decode_opaque_fixed() to return zero on success.
> This is the best fix.
Since the XDR field is fixed in size, the caller already knows how many
bytes were decoded, on success. Thus, xdr_stream_decode_opaque_fixed()
doesn't need to return that value. And, xdr_stream_decode_u32 and _u64
both return zero on success.
Since there is only one other caller, modifying the set of return values
of xdr_stream_decode_opaque_fixed() seems sensible to me.
But, I spot something else amiss here. Note that "sizeof(bex.vol_id)"
is the size of
595 struct nfsd4_deviceid {
596 u64 fsid_idx;
597 u32 generation;
598 u32 pad;
599 };
This is a C structure. The compiler is permitted to add padding, though
it probably doesn't in this simple case. Distributions are known to add
padding to internal structures to enable additions of new fields without
breaking ABI compatibility.
Thus there is no hard guarantee that sizeof(nfsd4_deviceid) will always
be exactly 16 bytes.
To ensure that the XDR stream is incremented the correct number of bytes
while decoding this field, the decoder needs to pass the size of the XDR
field, not the size of the in-memory C structure. Thankfully,
include/linux/nfs4.h has:
800 #define NFS4_DEVICEID4_SIZE 16
So let's use NFS4_DEVICEID4_SIZE rather than sizeof(bex.vol_id) when
rewriting the problematic decoder.
But that doesn't guarantee that the bex field (of type struct
nfsd4_deviceid) is large enough to receive the raw 16-byte XDR data. It
probably is, but why not write code that documents that assumption.
Maybe what this needs is a type-specific decoder for deviceid4 fields
that will take a char xx[16] off the wire and decode it properly into
the in-memory fsid_idx and generation fields in a struct nfsd4_deviceid.
We already do something similar for file handles.
> regards,
> dan carpenter
>
> 161 nfserr = nfserr_bad_xdr;
> 162 goto fail;
> 163 }
> 164
> 165 if (xdr_stream_decode_u64(xdr, &bex.foff)) {
> 166 nfserr = nfserr_bad_xdr;
> 167 goto fail;
> 168 }
> 169 if (bex.foff & (block_size - 1)) {
> 170 nfserr = nfserr_inval;
> 171 goto fail;
> 172 }
> 173
> 174 if (xdr_stream_decode_u64(xdr, &bex.len)) {
> 175 nfserr = nfserr_bad_xdr;
> 176 goto fail;
> 177 }
> 178 if (bex.len & (block_size - 1)) {
> 179 nfserr = nfserr_inval;
> 180 goto fail;
> 181 }
> 182
> 183 if (xdr_stream_decode_u64(xdr, &bex.soff)) {
> 184 nfserr = nfserr_bad_xdr;
> 185 goto fail;
> 186 }
> 187 if (bex.soff & (block_size - 1)) {
> 188 nfserr = nfserr_inval;
> 189 goto fail;
> 190 }
> 191
> 192 if (xdr_stream_decode_u32(xdr, &bex.es)) {
> 193 nfserr = nfserr_bad_xdr;
> 194 goto fail;
> 195 }
> 196 if (bex.es != PNFS_BLOCK_READWRITE_DATA) {
> 197 nfserr = nfserr_inval;
> 198 goto fail;
> 199 }
> 200
> 201 iomaps[i].offset = bex.foff;
> 202 iomaps[i].length = bex.len;
> 203 }
> 204
> 205 *iomapp = iomaps;
> 206 *nr_iomapsp = nr_iomaps;
> 207 return nfs_ok;
> 208 fail:
> 209 kfree(iomaps);
> 210 return nfserr;
> 211 }
>
>
--
Chuck Lever
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [bug report] nfsd: Implement large extent array support in pNFS
2025-07-16 20:31 ` Chuck Lever
@ 2025-07-17 9:51 ` Sergey Bashirov
0 siblings, 0 replies; 3+ messages in thread
From: Sergey Bashirov @ 2025-07-17 9:51 UTC (permalink / raw)
To: Chuck Lever, Dan Carpenter; +Cc: linux-nfs, Sergey Bashirov
Hi Chuck, Dan,
On Wed, Jul 16, 2025 at 04:31:49PM -0400, Chuck Lever wrote:
> On 7/16/25 3:40 PM, Dan Carpenter wrote:
> > #4: Change xdr_stream_decode_opaque_fixed() to return zero on success.
> > This is the best fix.
>
> Since the XDR field is fixed in size, the caller already knows how many
> bytes were decoded, on success. Thus, xdr_stream_decode_opaque_fixed()
> doesn't need to return that value. And, xdr_stream_decode_u32 and _u64
> both return zero on success.
>
> Since there is only one other caller, modifying the set of return values
> of xdr_stream_decode_opaque_fixed() seems sensible to me.
I like this solution too, will send patches to fix.
--
Sergey Bashirov
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-07-17 9:51 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-16 19:40 [bug report] nfsd: Implement large extent array support in pNFS Dan Carpenter
2025-07-16 20:31 ` Chuck Lever
2025-07-17 9:51 ` Sergey Bashirov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox