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
next 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox