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

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

 



On Wed, Aug 25, 2021 at 11:04:40AM +0300, Dan Carpenter wrote:
> 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.

And we should too. This was already in my todo list. Just didn't
notice real problem yet. I just though we should follow api as api
stated
	
	Flag checking should be done at the beginning of the ->fiemap
	callback via the fiemap_prep() helper.

Do you want to send a patch or do I? If I do it can I put reported-by
from you?

> 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.

Example ext4 want to check some stuff before fiemap_prep(). There is
probably good reason in some corner case.

> 
>     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