dmu: fix integer overflows

The params to the functions are uint64_t, but the offsets to memcpy
/ bcopy are calculated using 32bit ints. This patch changes them to
also be uint64_t so there isnt an overflow. PaX's Size Overflow
caught this when formatting a zvol.

Gentoo bug: #546490

PAX: offset: 1ffffb000 db->db_offset: 1ffffa000 db->db_size: 2000 size: 5000
PAX: size overflow detected in function dmu_read /var/tmp/portage/sys-fs/zfs-kmod-0.6.3-r1/work/zfs-zfs-0.6.3/module/zfs/../../module/zfs/dmu.c:781 cicus.366_146 max, count: 15
CPU: 1 PID: 2236 Comm: zvol/10 Tainted: P           O   3.17.7-hardened-r1 #1
Call Trace:
 [<ffffffffa0382ee8>] ? dsl_dataset_get_holds+0x9d58/0x343ce [zfs]
 [<ffffffff81a59c88>] dump_stack+0x4e/0x7a
 [<ffffffffa0393c2a>] ? dsl_dataset_get_holds+0x1aa9a/0x343ce [zfs]
 [<ffffffff81206696>] report_size_overflow+0x36/0x40
 [<ffffffffa02dba2b>] dmu_read+0x52b/0x920 [zfs]
 [<ffffffffa0373ad1>] zrl_is_locked+0x7d1/0x1ce0 [zfs]
 [<ffffffffa0364cd2>] zil_clean+0x9d2/0xc00 [zfs]
 [<ffffffffa0364f21>] zil_commit+0x21/0x30 [zfs]
 [<ffffffffa0373fe1>] zrl_is_locked+0xce1/0x1ce0 [zfs]
 [<ffffffff81a5e2c7>] ? __schedule+0x547/0xbc0
 [<ffffffffa01582e6>] taskq_cancel_id+0x2a6/0x5b0 [spl]
 [<ffffffff81103eb0>] ? wake_up_state+0x20/0x20
 [<ffffffffa0158150>] ? taskq_cancel_id+0x110/0x5b0 [spl]
 [<ffffffff810f7ff4>] kthread+0xc4/0xe0
 [<ffffffff810f7f30>] ? kthread_create_on_node+0x170/0x170
 [<ffffffff81a62fa4>] ret_from_fork+0x74/0xa0
 [<ffffffff810f7f30>] ? kthread_create_on_node+0x170/0x170

Signed-off-by: Jason Zaman <jason@perfinion.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #3333
This commit is contained in:
Jason Zaman 2015-04-30 16:20:38 +04:00 committed by Brian Behlendorf
parent 98b254188a
commit c9520ecc0f
1 changed files with 22 additions and 18 deletions

View File

@ -764,7 +764,7 @@ dmu_read(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
* handle that here as well. * handle that here as well.
*/ */
if (dn->dn_maxblkid == 0) { if (dn->dn_maxblkid == 0) {
int newsz = offset > dn->dn_datablksz ? 0 : uint64_t newsz = offset > dn->dn_datablksz ? 0 :
MIN(size, dn->dn_datablksz - offset); MIN(size, dn->dn_datablksz - offset);
bzero((char *)buf + newsz, size - newsz); bzero((char *)buf + newsz, size - newsz);
size = newsz; size = newsz;
@ -784,16 +784,16 @@ dmu_read(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
break; break;
for (i = 0; i < numbufs; i++) { for (i = 0; i < numbufs; i++) {
int tocpy; uint64_t tocpy;
int bufoff; int64_t bufoff;
dmu_buf_t *db = dbp[i]; dmu_buf_t *db = dbp[i];
ASSERT(size > 0); ASSERT(size > 0);
bufoff = offset - db->db_offset; bufoff = offset - db->db_offset;
tocpy = (int)MIN(db->db_size - bufoff, size); tocpy = MIN(db->db_size - bufoff, size);
bcopy((char *)db->db_data + bufoff, buf, tocpy); (void) memcpy(buf, (char *)db->db_data + bufoff, tocpy);
offset += tocpy; offset += tocpy;
size -= tocpy; size -= tocpy;
@ -819,14 +819,14 @@ dmu_write(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
FALSE, FTAG, &numbufs, &dbp)); FALSE, FTAG, &numbufs, &dbp));
for (i = 0; i < numbufs; i++) { for (i = 0; i < numbufs; i++) {
int tocpy; uint64_t tocpy;
int bufoff; int64_t bufoff;
dmu_buf_t *db = dbp[i]; dmu_buf_t *db = dbp[i];
ASSERT(size > 0); ASSERT(size > 0);
bufoff = offset - db->db_offset; bufoff = offset - db->db_offset;
tocpy = (int)MIN(db->db_size - bufoff, size); tocpy = MIN(db->db_size - bufoff, size);
ASSERT(i == 0 || i == numbufs-1 || tocpy == db->db_size); ASSERT(i == 0 || i == numbufs-1 || tocpy == db->db_size);
@ -1108,13 +1108,15 @@ dmu_read_req(objset_t *os, uint64_t object, struct request *req)
req_offset = 0; req_offset = 0;
for (i = 0; i < numbufs; i++) { for (i = 0; i < numbufs; i++) {
int tocpy, didcpy, bufoff; uint64_t tocpy;
int64_t bufoff;
int didcpy;
dmu_buf_t *db = dbp[i]; dmu_buf_t *db = dbp[i];
bufoff = offset - db->db_offset; bufoff = offset - db->db_offset;
ASSERT3S(bufoff, >=, 0); ASSERT3S(bufoff, >=, 0);
tocpy = (int)MIN(db->db_size - bufoff, size); tocpy = MIN(db->db_size - bufoff, size);
if (tocpy == 0) if (tocpy == 0)
break; break;
@ -1156,13 +1158,15 @@ dmu_write_req(objset_t *os, uint64_t object, struct request *req, dmu_tx_t *tx)
req_offset = 0; req_offset = 0;
for (i = 0; i < numbufs; i++) { for (i = 0; i < numbufs; i++) {
int tocpy, didcpy, bufoff; uint64_t tocpy;
int64_t bufoff;
int didcpy;
dmu_buf_t *db = dbp[i]; dmu_buf_t *db = dbp[i];
bufoff = offset - db->db_offset; bufoff = offset - db->db_offset;
ASSERT3S(bufoff, >=, 0); ASSERT3S(bufoff, >=, 0);
tocpy = (int)MIN(db->db_size - bufoff, size); tocpy = MIN(db->db_size - bufoff, size);
if (tocpy == 0) if (tocpy == 0)
break; break;
@ -1212,14 +1216,14 @@ dmu_read_uio(objset_t *os, uint64_t object, uio_t *uio, uint64_t size)
return (err); return (err);
for (i = 0; i < numbufs; i++) { for (i = 0; i < numbufs; i++) {
int tocpy; uint64_t tocpy;
int bufoff; int64_t bufoff;
dmu_buf_t *db = dbp[i]; dmu_buf_t *db = dbp[i];
ASSERT(size > 0); ASSERT(size > 0);
bufoff = uio->uio_loffset - db->db_offset; bufoff = uio->uio_loffset - db->db_offset;
tocpy = (int)MIN(db->db_size - bufoff, size); tocpy = MIN(db->db_size - bufoff, size);
if (xuio) { if (xuio) {
dmu_buf_impl_t *dbi = (dmu_buf_impl_t *)db; dmu_buf_impl_t *dbi = (dmu_buf_impl_t *)db;
@ -1263,14 +1267,14 @@ dmu_write_uio_dnode(dnode_t *dn, uio_t *uio, uint64_t size, dmu_tx_t *tx)
return (err); return (err);
for (i = 0; i < numbufs; i++) { for (i = 0; i < numbufs; i++) {
int tocpy; uint64_t tocpy;
int bufoff; int64_t bufoff;
dmu_buf_t *db = dbp[i]; dmu_buf_t *db = dbp[i];
ASSERT(size > 0); ASSERT(size > 0);
bufoff = uio->uio_loffset - db->db_offset; bufoff = uio->uio_loffset - db->db_offset;
tocpy = (int)MIN(db->db_size - bufoff, size); tocpy = MIN(db->db_size - bufoff, size);
ASSERT(i == 0 || i == numbufs-1 || tocpy == db->db_size); ASSERT(i == 0 || i == numbufs-1 || tocpy == db->db_size);