aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian-Gruenbichler <f.gruenbichler@proxmox.com>2024-03-29 21:37:40 +0000
committerGitHub <noreply@github.com>2024-03-29 21:37:40 +0000
commitc0aab8b8f91f5ecb2c625a8fa7265f26c260e10a (patch)
tree042d244f699dbda976dfe244b9977b1d64b9c8c1
parentb1e46f869e773086c23c565d7d5b261577023cfb (diff)
downloadsrc-c0aab8b8f91f5ecb2c625a8fa7265f26c260e10a.tar.gz
src-c0aab8b8f91f5ecb2c625a8fa7265f26c260e10a.zip
zvols: prevent overflow of minor device numbers
currently, the linux kernel allows 2^20 minor devices per major device number. ZFS reserves blocks of 2^4 minors per zvol: 1 for the zvol itself, the other 15 for the first partitions of that zvol. as a result, only 2^16 such blocks are available for use. there are no checks in place to avoid overflowing into the major device number when more than 2^16 zvols are allocated (with volmode=dev or default). instead of ignoring this limit, which comes with all sorts of weird knock-on effects, detect this situation and simply fail allocating the zvol block device early on. without this safeguard, the kernel will reject the attempt to create an already existing block device, but ZFS doesn't handle this error and gets confused about which zvol occupies which minor slot, potentially resulting in kernel NULL derefs and other issues later on. Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> Closes #16006
-rw-r--r--module/os/linux/zfs/zvol_os.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/module/os/linux/zfs/zvol_os.c b/module/os/linux/zfs/zvol_os.c
index 8d5d1f06fce9..26cc63d426eb 100644
--- a/module/os/linux/zfs/zvol_os.c
+++ b/module/os/linux/zfs/zvol_os.c
@@ -1314,6 +1314,13 @@ zvol_os_create_minor(const char *name)
if (idx < 0)
return (SET_ERROR(-idx));
minor = idx << ZVOL_MINOR_BITS;
+ if (MINOR(minor) != minor) {
+ /* too many partitions can cause an overflow */
+ zfs_dbgmsg("zvol: create minor overflow: %s, minor %u/%u",
+ name, minor, MINOR(minor));
+ ida_simple_remove(&zvol_ida, idx);
+ return (SET_ERROR(EINVAL));
+ }
zv = zvol_find_by_name_hash(name, hash, RW_NONE);
if (zv) {