]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb/client: handle overlapping allocated ranges in fallocate
authorHuiwen He <hehuiwen@kylinos.cn>
Fri, 3 Jul 2026 05:32:56 +0000 (13:32 +0800)
committerSteve French <stfrench@microsoft.com>
Mon, 13 Jul 2026 15:37:54 +0000 (10:37 -0500)
smb3_simple_fallocate_range() can skip holes when an allocated range
returned by the server starts before the current fallocate offset. The
skipped hole is not zero-filled, but fallocate still returns success. A
later write to that hole may therefore fail with ENOSPC.

The function queries allocated ranges so that it can preserve existing
contents and write zeroes only into holes. However, the server may return
a range that starts before the current fallocate offset.

For example, assume the fallocate request is [100, 400) and the only
allocated range returned by the server is [0, 200):

        Request:      [100, 400)
        Server range: [  0, 200)  allocated

        Correct:
        [100, 200)    allocated data, skip
        [200, 400)    hole, zero-fill

        Current:
        [100, 300)    skipped
        [300, 400)    zero-filled afterwards

The current code adds the full server range length, 200, to the current
offset 100 and moves to 300. As a result, the hole in [200, 300) is
skipped without being zero-filled.

Fix this by advancing only over the part of the allocated range that
overlaps the current fallocate offset.  Ignore ranges that end before the
current offset and reject ranges whose end offset overflows.

This also prevents a malformed range length from causing an out-of-bounds
zero-buffer read.

Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation")
Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/smb2ops.c

index 9bf57a014744f131d1129d8140856ed409f7c7f1..ddb28464ff81874b413d5bb51439a02f9f1fbf9e 100644 (file)
@@ -3611,6 +3611,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
        struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
        u32 out_data_len;
        char *buf = NULL;
+       u64 range_start, range_len, range_end;
        loff_t l;
        int rc;
 
@@ -3647,13 +3648,21 @@ static int smb3_simple_fallocate_range(unsigned int xid,
                        goto out;
                }
 
-               if (off < le64_to_cpu(tmp_data->file_offset)) {
+               range_start = le64_to_cpu(tmp_data->file_offset);
+               range_len = le64_to_cpu(tmp_data->length);
+               if (check_add_overflow(range_start, range_len, &range_end) ||
+                   range_end > S64_MAX) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+
+               if (off < range_start) {
                        /*
                         * We are at a hole. Write until the end of the region
                         * or until the next allocated data,
                         * whichever comes next.
                         */
-                       l = le64_to_cpu(tmp_data->file_offset) - off;
+                       l = range_start - off;
                        if (len < l)
                                l = len;
                        rc = smb3_simple_fallocate_write_range(xid, tcon,
@@ -3670,11 +3679,13 @@ static int smb3_simple_fallocate_range(unsigned int xid,
                 * until the end of the data or the end of the region
                 * we are supposed to fallocate, whichever comes first.
                 */
-               l = le64_to_cpu(tmp_data->length);
-               if (len < l)
-                       l = len;
-               off += l;
-               len -= l;
+               if (off < range_end) {
+                       l = range_end - off;
+                       if (len < l)
+                               l = len;
+                       off += l;
+                       len -= l;
+               }
 
                tmp_data = &tmp_data[1];
                out_data_len -= sizeof(struct file_allocated_range_buffer);