]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/fileeq: Prevent OOB with short read files
authorTobias Stoeckmann <tobias@stoeckmann.org>
Wed, 18 Feb 2026 17:04:54 +0000 (18:04 +0100)
committerTobias Stoeckmann <tobias@stoeckmann.org>
Wed, 18 Feb 2026 20:42:55 +0000 (21:42 +0100)
Using the Linux Crypto API stores hash sums of read chunks in memory. It
trusts that get_digest is called as often as "read blocks" exist in file.

It becomes an issue with files which have less content than st_size
suggests, e.g. files in sysfs. Eventually, the iterator n will be
eq->blocksmax, because the EOF is never noticed. Writing this next hash
sum into memory would lead to an out of boundary write.

Verify that n is less than eq->blocksmax. If this is not true anymore,
treat it as an error so files are considered to be not equal. This is
much safer than erroneously treating them as equal.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
lib/fileeq.c

index 7626195637a3ae9fc5f17c692fac15ae115ed30d..67c5c747db1e896d63c272852cc810ed5b21942e 100644 (file)
@@ -367,9 +367,12 @@ static ssize_t read_block(struct ul_fileeq *eq, struct ul_fileeq_data *data,
        off_t off = 0;
        ssize_t rsz;
 
-       if (data->is_eof || n > eq->blocksmax)
+       if (data->is_eof)
                return 0;
 
+       if (n >= eq->blocksmax)
+               return -EINVAL;
+
        fd = get_fd(eq, data, &off);
        if (fd < 0)
                return fd;
@@ -406,9 +409,6 @@ static ssize_t get_digest(struct ul_fileeq *eq, struct ul_fileeq_data *data,
        size_t sz;
        int fd;
 
-       if (n > eq->blocksmax)
-               return 0;
-
        /* return already cached if available */
        if (n < get_cached_nblocks(data)) {
                DBG(DATA, ul_debugobj(data, " digest cached"));
@@ -422,6 +422,9 @@ static ssize_t get_digest(struct ul_fileeq *eq, struct ul_fileeq_data *data,
                return 0;
        }
 
+       if (n >= eq->blocksmax)
+               return -EINVAL;
+
        /* read new block */
        fd = get_fd(eq, data, &off);
        if (fd < 0)
@@ -438,8 +441,6 @@ static ssize_t get_digest(struct ul_fileeq *eq, struct ul_fileeq_data *data,
                        return -ENOMEM;
        }
 
-       assert(n <= eq->blocksmax);
-
        rsz = sendfile(eq->fd_cip, data->fd, NULL, eq->readsiz);
        DBG(DATA, ul_debugobj(data, "  sent %zu [%zu wanted] to cipher", rsz, eq->readsiz));