All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dan Carpenter <dan.carpenter@oracle.com>
To: almaz.alexandrovich@paragon-software.com
Cc: ntfs3@lists.linux.dev
Subject: [bug report] fs/ntfs3: integer overflow in ni_fiemap()
Date: Wed, 25 Aug 2021 11:04:40 +0300	[thread overview]
Message-ID: <20210825080440.GA17407@kili> (raw)

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

             reply	other threads:[~2021-08-25  8:05 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-25  8:04 Dan Carpenter [this message]
2021-08-25  8:33 ` [bug report] fs/ntfs3: integer overflow in ni_fiemap() Kari Argillander
2021-08-25  8:35   ` Dan Carpenter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210825080440.GA17407@kili \
    --to=dan.carpenter@oracle.com \
    --cc=almaz.alexandrovich@paragon-software.com \
    --cc=ntfs3@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.