[bug report] fs/ntfs3: integer overflow in ni_fiemap()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello Konstantin Komarov,

The patch 4342306f0f0d: "fs/ntfs3: Add file operations and
implementation" from Aug 13, 2021, leads to the following
Smatch static checker warning:

	fs/ntfs3/frecord.c:1894 ni_fiemap()
	warn: potential integer overflow from user 'vbo + len'

fs/ntfs3/frecord.c
    1843 int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
    1844 	      __u64 vbo, __u64 len)

"vbo" and "len" are u64 values which are controlled by the user from
ioctl_fiemap().

I looked at how BTRFS does it and it uses the fiemap_prep() function.
To be honest, I don't know why fiemap_prep() isn't used in ioctl_fiemap()
because that seems safer than relying on filesystems to do it themselves.

    1845 {
    1846 	int err = 0;
    1847 	struct ntfs_sb_info *sbi = ni->mi.sbi;
    1848 	u8 cluster_bits = sbi->cluster_bits;
    1849 	struct runs_tree *run;
    1850 	struct rw_semaphore *run_lock;
    1851 	struct ATTRIB *attr;
    1852 	CLST vcn = vbo >> cluster_bits;
    1853 	CLST lcn, clen;
    1854 	u64 valid = ni->i_valid;
    1855 	u64 lbo, bytes;
    1856 	u64 end, alloc_size;
    1857 	size_t idx = -1;
    1858 	u32 flags;
    1859 	bool ok;
    1860 
    1861 	if (S_ISDIR(ni->vfs_inode.i_mode)) {
    1862 		run = &ni->dir.alloc_run;
    1863 		attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME,
    1864 				    ARRAY_SIZE(I30_NAME), NULL, NULL);
    1865 		run_lock = &ni->dir.run_lock;
    1866 	} else {
    1867 		run = &ni->file.run;
    1868 		attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL,
    1869 				    NULL);
    1870 		if (!attr) {
    1871 			err = -EINVAL;
    1872 			goto out;
    1873 		}
    1874 		if (is_attr_compressed(attr)) {
    1875 			/*unfortunately cp -r incorrectly treats compressed clusters*/
    1876 			err = -EOPNOTSUPP;
    1877 			ntfs_inode_warn(
    1878 				&ni->vfs_inode,
    1879 				"fiemap is not supported for compressed file (cp -r)");
    1880 			goto out;
    1881 		}
    1882 		run_lock = &ni->file.run_lock;
    1883 	}
    1884 
    1885 	if (!attr || !attr->non_res) {
    1886 		err = fiemap_fill_next_extent(
    1887 			fieinfo, 0, 0,
    1888 			attr ? le32_to_cpu(attr->res.data_size) : 0,
    1889 			FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_LAST |
    1890 				FIEMAP_EXTENT_MERGED);
    1891 		goto out;
    1892 	}
    1893 
--> 1894 	end = vbo + len;
                ^^^^^^^^^^^^^^^

This can overflow.

    1895 	alloc_size = le64_to_cpu(attr->nres.alloc_size);
    1896 	if (end > alloc_size)
    1897 		end = alloc_size;
    1898 
    1899 	down_read(run_lock);
    1900 
    1901 	while (vbo < end) {
    1902 		if (idx == -1) {
    1903 			ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx);
    1904 		} else {
    1905 			CLST vcn_next = vcn;
    1906 
    1907 			ok = run_get_entry(run, ++idx, &vcn, &lcn, &clen) &&
    1908 			     vcn == vcn_next;
    1909 			if (!ok)
    1910 				vcn = vcn_next;
    1911 		}
    1912 
    1913 		if (!ok) {
    1914 			up_read(run_lock);
    1915 			down_write(run_lock);
    1916 
    1917 			err = attr_load_runs_vcn(ni, attr->type,
    1918 						 attr_name(attr),
    1919 						 attr->name_len, run, vcn);
    1920 
    1921 			up_write(run_lock);
    1922 			down_read(run_lock);
    1923 
    1924 			if (err)
    1925 				break;
    1926 
    1927 			ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx);
    1928 
    1929 			if (!ok) {
    1930 				err = -EINVAL;
    1931 				break;
    1932 			}
    1933 		}
    1934 
    1935 		if (!clen) {
    1936 			err = -EINVAL; // ?
    1937 			break;
    1938 		}
    1939 
    1940 		if (lcn == SPARSE_LCN) {
    1941 			vcn += clen;
    1942 			vbo = (u64)vcn << cluster_bits;
    1943 			continue;
    1944 		}
    1945 
    1946 		flags = FIEMAP_EXTENT_MERGED;
    1947 		if (S_ISDIR(ni->vfs_inode.i_mode)) {
    1948 			;
    1949 		} else if (is_attr_compressed(attr)) {
    1950 			CLST clst_data;
    1951 
    1952 			err = attr_is_frame_compressed(
    1953 				ni, attr, vcn >> attr->nres.c_unit, &clst_data);
    1954 			if (err)
    1955 				break;
    1956 			if (clst_data < NTFS_LZNT_CLUSTERS)
    1957 				flags |= FIEMAP_EXTENT_ENCODED;
    1958 		} else if (is_attr_encrypted(attr)) {
    1959 			flags |= FIEMAP_EXTENT_DATA_ENCRYPTED;
    1960 		}
    1961 
    1962 		vbo = (u64)vcn << cluster_bits;
    1963 		bytes = (u64)clen << cluster_bits;
    1964 		lbo = (u64)lcn << cluster_bits;
    1965 
    1966 		vcn += clen;
    1967 
    1968 		if (vbo + bytes >= end) {
    1969 			bytes = end - vbo;
    1970 			flags |= FIEMAP_EXTENT_LAST;
    1971 		}
    1972 
    1973 		if (vbo + bytes <= valid) {
    1974 			;
    1975 		} else if (vbo >= valid) {
    1976 			flags |= FIEMAP_EXTENT_UNWRITTEN;
    1977 		} else {
    1978 			/* vbo < valid && valid < vbo + bytes */
    1979 			u64 dlen = valid - vbo;
    1980 
    1981 			err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen,
    1982 						      flags);
    1983 			if (err < 0)
    1984 				break;
    1985 			if (err == 1) {
    1986 				err = 0;
    1987 				break;
    1988 			}
    1989 
    1990 			vbo = valid;
    1991 			bytes -= dlen;
    1992 			if (!bytes)
    1993 				continue;
    1994 
    1995 			lbo += dlen;
    1996 			flags |= FIEMAP_EXTENT_UNWRITTEN;
    1997 		}
    1998 
    1999 		err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
    2000 		if (err < 0)
    2001 			break;
    2002 		if (err == 1) {
    2003 			err = 0;
    2004 			break;
    2005 		}
    2006 
    2007 		vbo += bytes;
    2008 	}
    2009 
    2010 	up_read(run_lock);
    2011 
    2012 out:
    2013 	return err;
    2014 }

regards,
dan carpenter




[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux