From patchwork Fri Oct 18 09:43:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Potapenko X-Patchwork-Id: 11198011 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5B2F414ED for ; Fri, 18 Oct 2019 09:44:25 +0000 (UTC) Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.kernel.org (Postfix) with ESMTP id 0D86520820 for ; Fri, 18 Oct 2019 09:44:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="HYhJafQL" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 0D86520820 Authentication-Results: mail.kernel.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=owner-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix) id B64DE8E0032; Fri, 18 Oct 2019 05:44:23 -0400 (EDT) Delivered-To: linux-mm-outgoing@kvack.org Received: by kanga.kvack.org (Postfix, from userid 40) id B13198E0003; Fri, 18 Oct 2019 05:44:23 -0400 (EDT) X-Original-To: int-list-linux-mm@kvack.org X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id A01E08E0032; Fri, 18 Oct 2019 05:44:23 -0400 (EDT) X-Original-To: linux-mm@kvack.org X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0248.hostedemail.com [216.40.44.248]) by kanga.kvack.org (Postfix) with ESMTP id 783188E0003 for ; Fri, 18 Oct 2019 05:44:23 -0400 (EDT) Received: from smtpin24.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay01.hostedemail.com (Postfix) with SMTP id 0CF5D18339497 for ; Fri, 18 Oct 2019 09:44:23 +0000 (UTC) X-FDA: 76056420006.24.fall10_7d7ba2fad084f X-Spam-Summary: 2,0,0,e8412ddb5f7ea80d,d41d8cd98f00b204,3dimpxqykccedifabodlldib.9ljifkru-jjhs79h.lod@flex--glider.bounces.google.com,:glider@google.com:akpm@linux-foundation.org:axboe@kernel.dk:tytso@mit.edu:dmitry.torokhov@gmail.com:martin.petersen@oracle.com:mst@redhat.com:hch@lst.de:edumazet@google.com:ericvh@gmail.com:tiwai@suse.com:vegard.nossum@oracle.com:dvyukov@google.com:,RULES_HIT:4:41:152:355:371:372:379:541:800:960:966:973:988:989:1260:1277:1313:1314:1345:1359:1431:1437:1516:1518:1593:1594:1605:1730:1747:1777:1792:1981:2194:2196:2198:2199:2200:2201:2393:2559:2562:2640:2693:2731:2898:2899:2904:3138:3139:3140:3141:3142:3152:3865:3866:3867:3868:3870:3871:3872:3874:4250:4321:4385:4605:5007:6119:6120:6261:6653:6742:7514:7901:7903:8603:8957:9036:9969:10004:11026:11232:11473:11658:11914:12043:12291:12296:12297:12438:12555:12679:12895:12986:13846:14096:14097:14394:14659:21060:21080:21324:21365:21433:21444:21451:21611:21627:21740:30003:30012:30045:30054:30064:30070,0,RBL: 209.85.2 X-HE-Tag: fall10_7d7ba2fad084f X-Filterd-Recvd-Size: 17386 Received: from mail-wr1-f73.google.com (mail-wr1-f73.google.com [209.85.221.73]) by imf41.hostedemail.com (Postfix) with ESMTP for ; Fri, 18 Oct 2019 09:44:22 +0000 (UTC) Received: by mail-wr1-f73.google.com with SMTP id v8so1483043wrt.16 for ; Fri, 18 Oct 2019 02:44:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=pOUwjd35nKVvjlhgx4bOQKBVbyC5r5+P6npeyEw8CAk=; b=HYhJafQL39sNEPe62Nwbvi8F4yhrg/df8kQfWghQlkBFPH3Ot4u7rGBUeup6WJ0xV3 d1sGpfh2xgIotFrmSmgF3lsmXyMWBHdcvu3lUxt4P3+1yXFtHKgoU8CZHzF1hXfjpEs2 bOM9OAAUm0fBgt4dB9jO91UqMCfjCZ6XGmJnZt33QVCVuGzU0DF8GA9oNfdOFWSBb1X0 KqKVvTWyr3t4m9Shxr7soQkgkSRuwvKvCGN0As+J5ytAq81LCWC6DT+qq118ztPSb3lD mbjNFGFrVz85rPGPXRvmQ55I9BTQQWOscdnhDad32GWbELJNVmB23DO8dfbtRfH60fjC 9e1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=pOUwjd35nKVvjlhgx4bOQKBVbyC5r5+P6npeyEw8CAk=; b=skeQTYtzWMAcUTY1eqWsXZXJXcNLg4sRwtRPQ8pl46y05UbyI6rJAYOLfaJoeDSko+ GNDbRIRsvrYKYfEPB+N5ZuxWKgs+tjsd1dLxk90aZzKpAsEnzkR+hlNcckZoF2J+eYTW 11m4fcsY6LL5oR3y0e57lFd9SkxUO9eIXAlfXOArNQzUGGapqcNbexcZzqux65L3rc1j 46Evzwr2grECZpgf+WszVbFkrigS+w0RATCVj0qFBXOUrmw2mJQPstE0TgsO+LA8edvL VCGZ5XMyuX3enqBLHwdIcXz2tPSVu1qxHsKnlsliMwKZAnbKUadjYMkNOVwco62x3VV+ hQZg== X-Gm-Message-State: APjAAAVE4x12eEhjb8Z27yi9Gf+onUxTyQPA89yrj8q9SauR6FlEYKHP 1f5xSZcFB3IsB7pvYrz1FhuKk7btsus= X-Google-Smtp-Source: APXvYqyLa7pFC59EamOyCDumBq0lgnLY7TtX//hXM1PxBd9RSRqFw2Nm7BbctyNlsW0werqlPZvL3yh/sjE= X-Received: by 2002:a5d:51d2:: with SMTP id n18mr6872637wrv.225.1571391860987; Fri, 18 Oct 2019 02:44:20 -0700 (PDT) Date: Fri, 18 Oct 2019 11:43:01 +0200 In-Reply-To: <20191018094304.37056-1-glider@google.com> Message-Id: <20191018094304.37056-24-glider@google.com> Mime-Version: 1.0 References: <20191018094304.37056-1-glider@google.com> X-Mailer: git-send-email 2.23.0.866.gb869b98d4c-goog Subject: [PATCH RFC v1 23/26] kmsan: unpoisoning buffers from devices etc. From: glider@google.com To: Alexander Potapenko Cc: Andrew Morton , Jens Axboe , "Theodore Ts'o" , Dmitry Torokhov , "Martin K . Petersen" , "Michael S. Tsirkin" , Christoph Hellwig , Eric Dumazet , Eric Van Hensbergen , Takashi Iwai , Vegard Nossum , Dmitry Vyukov , linux-mm@kvack.org X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: When data is copied to memory from a device KMSAN should treat it as initialized. In most cases it's enough to just unpoison the buffer that is known to come from a device. In the case with __do_page_cache_readahead() and bio_copy_user_iov() we have to mark the whole pages as ignored by KMSAN, as it's not obvious where these pages are read again. Signed-off-by: Alexander Potapenko To: Alexander Potapenko Cc: Andrew Morton Cc: Jens Axboe Cc: "Theodore Ts'o" Cc: Dmitry Torokhov Cc: Martin K. Petersen Cc: "Michael S. Tsirkin" Cc: Christoph Hellwig Cc: Eric Dumazet Cc: Eric Van Hensbergen Cc: Takashi Iwai Cc: Vegard Nossum Cc: Dmitry Vyukov Cc: linux-mm@kvack.org --- Suggestions on how to avoid scattering too many annotations over the code are welcome. Perhaps annotating DMA reads/writes is enough to get rid of most of these annotations. Change-Id: Id460e7a86ce564f1357469f53d0c7410ca08f0e9 --- block/bio.c | 20 ++++++++++++++++++++ block/partition-generic.c | 9 ++++++++- drivers/char/random.c | 2 ++ drivers/input/serio/libps2.c | 6 +++++- drivers/scsi/scsi_lib.c | 4 ++++ drivers/usb/core/message.c | 6 +++++- drivers/virtio/virtio_ring.c | 14 ++++++++++++++ fs/buffer.c | 7 ++++++- include/linux/dma-mapping.h | 2 ++ include/linux/skbuff.h | 5 ++++- mm/filemap.c | 3 +++ mm/readahead.c | 6 ++++++ net/9p/protocol.c | 2 ++ sound/core/oss/pcm_oss.c | 7 +++++++ 14 files changed, 88 insertions(+), 5 deletions(-) diff --git a/block/bio.c b/block/bio.c index 8f0ed6228fc5..40773cf4b50c 100644 --- a/block/bio.c +++ b/block/bio.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -900,6 +901,13 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) __bio_add_page(bio, page, len, offset); } offset = 0; + /* + * TODO(glider): these pages will be soon passed to block + * device for reading, so consider them initialized. + */ + if (iov_iter_rw(iter) == READ) + kmsan_unpoison_shadow(page_address(page), + PAGE_SIZE); } iov_iter_advance(iter, size); @@ -1274,11 +1282,16 @@ struct bio *bio_copy_user_iov(struct request_queue *q, i++; } else { + /* + * TODO(glider): KMSAN doesn't track the pages + * allocated for bio here. + */ page = alloc_page(q->bounce_gfp | gfp_mask); if (!page) { ret = -ENOMEM; break; } + kmsan_ignore_page(page, /*order*/0); } if (bio_add_pc_page(q, bio, page, bytes, offset) < bytes) { @@ -1574,6 +1587,13 @@ struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, if (!page) goto cleanup; + /* + * TODO(glider): if we're about to read data from a SCSI device, + * assume the page allocated for that is already initialized. + */ + if (reading) + kmsan_unpoison_shadow(page_address(page), PAGE_SIZE); + if (!reading) memcpy(page_address(page), p, bytes); diff --git a/block/partition-generic.c b/block/partition-generic.c index aee643ce13d1..87b3ae9e7727 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -663,13 +663,20 @@ unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p) { struct address_space *mapping = bdev->bd_inode->i_mapping; struct page *page; + unsigned char *retval; page = read_mapping_page(mapping, (pgoff_t)(n >> (PAGE_SHIFT-9)), NULL); if (!IS_ERR(page)) { if (PageError(page)) goto fail; p->v = page; - return (unsigned char *)page_address(page) + ((n & ((1 << (PAGE_SHIFT - 9)) - 1)) << 9); + retval = (unsigned char *)page_address(page) + + ((n & ((1 << (PAGE_SHIFT - 9)) - 1)) << 9); + /* + * Unpoison sector-sized chunk of memory coming from the device. + */ + kmsan_unpoison_shadow(retval, SECTOR_SIZE); + return retval; fail: put_page(page); } diff --git a/drivers/char/random.c b/drivers/char/random.c index de434feb873a..cc4afc0f1039 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -320,6 +320,7 @@ #include #include #include +#include #include #include #include @@ -1061,6 +1062,7 @@ static void _extract_crng(struct crng_state *crng, spin_lock_irqsave(&crng->lock, flags); if (arch_get_random_long(&v)) crng->state[14] ^= v; + kmsan_unpoison_shadow(crng->state, sizeof(crng->state)); chacha20_block(&crng->state[0], out); if (crng->state[12] == 0) crng->state[13]++; diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index a8c94a940a79..80a9e0a9d3c3 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -30,6 +31,7 @@ static int ps2_do_sendbyte(struct ps2dev *ps2dev, u8 byte, int error; lockdep_assert_held(&ps2dev->serio->lock); + kmsan_check_memory(&byte, 1); do { ps2dev->nak = 1; @@ -294,9 +296,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command) serio_pause_rx(ps2dev->serio); - if (param) + if (param) { for (i = 0; i < receive; i++) param[i] = ps2dev->cmdbuf[(receive - 1) - i]; + kmsan_unpoison_shadow(param, receive); + } if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) { diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index dc210b9d4896..4235b317ac5f 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -296,6 +297,9 @@ int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, ret = rq->result; out: blk_put_request(req); + /* TODO(glider): this is a bit rough. */ + if (data_direction == DMA_FROM_DEVICE) + kmsan_unpoison_shadow(buffer, bufflen); return ret; } diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5adf489428aa..f1c3f8b3cfae 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -101,8 +102,11 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, retv = usb_start_wait_urb(urb, timeout, &length); if (retv < 0) return retv; - else + else { + /* TODO(glider): USB initializes |length| bytes? */ + kmsan_unpoison_shadow(data, length); return length; + } } /** diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index bdc08244a648..8b7ddc43618f 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #ifdef DEBUG @@ -387,6 +388,12 @@ static void vring_unmap_one_split(const struct vring_virtqueue *vq, (flags & VRING_DESC_F_WRITE) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } + /* + * Unmapping DMA memory after a transfer from device requires this + * memory to be unpoisoned. + */ + if (flags & VRING_DESC_F_WRITE) + kmsan_unpoison_shadow((const void *)desc->addr, desc->len); } static struct vring_desc *alloc_indirect_split(struct virtqueue *_vq, @@ -500,6 +507,13 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, desc[i].flags = cpu_to_virtio16(_vq->vdev, VRING_DESC_F_NEXT | VRING_DESC_F_WRITE); desc[i].addr = cpu_to_virtio64(_vq->vdev, addr); desc[i].len = cpu_to_virtio32(_vq->vdev, sg->length); + /* + * It's hard to figure out the buffer's address upon + * receive. Instead we unpoison it once, when exposing + * it to the device, and hope nobody else will write to + * it. + */ + kmsan_unpoison_shadow(sg_virt(sg), sg->length); prev = i; i = virtio16_to_cpu(_vq->vdev, desc[i].next); } diff --git a/fs/buffer.c b/fs/buffer.c index 86a38b979323..c08fb1a77fa1 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1170,14 +1170,17 @@ static struct buffer_head *__bread_slow(struct buffer_head *bh) lock_buffer(bh); if (buffer_uptodate(bh)) { unlock_buffer(bh); + kmsan_unpoison_shadow(bh->b_data, bh->b_size); return bh; } else { get_bh(bh); bh->b_end_io = end_buffer_read_sync; submit_bh(REQ_OP_READ, 0, bh); wait_on_buffer(bh); - if (buffer_uptodate(bh)) + if (buffer_uptodate(bh)) { + kmsan_unpoison_shadow(bh->b_data, bh->b_size); return bh; + } } brelse(bh); return NULL; @@ -1320,6 +1323,8 @@ __getblk_gfp(struct block_device *bdev, sector_t block, might_sleep(); if (bh == NULL) bh = __getblk_slow(bdev, block, size, gfp); + if (bh) + kmsan_unpoison_shadow(bh->b_data, bh->b_size); return bh; } EXPORT_SYMBOL(__getblk_gfp); diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 4a1c4fca475a..cc7b05eb024f 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -281,6 +282,7 @@ static inline dma_addr_t dma_map_page_attrs(struct device *dev, const struct dma_map_ops *ops = get_dma_ops(dev); dma_addr_t addr; + kmsan_unpoison_shadow(page_address(page), size); BUG_ON(!valid_dma_direction(dir)); if (dma_is_direct(ops)) addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4351577b14d7..b8e9cd697248 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -11,6 +11,7 @@ #define _LINUX_SKBUFF_H #include +#include #include #include #include @@ -2214,6 +2215,8 @@ static inline void *skb_put_data(struct sk_buff *skb, const void *data, { void *tmp = skb_put(skb, len); + /* Unpoison the data received from the network device. */ + kmsan_unpoison_shadow(data, len); memcpy(tmp, data, len); return tmp; @@ -2221,7 +2224,7 @@ static inline void *skb_put_data(struct sk_buff *skb, const void *data, static inline void skb_put_u8(struct sk_buff *skb, u8 val) { - *(u8 *)skb_put(skb, 1) = val; + *(u8 *)skb_put(skb, 1) = KMSAN_INIT_VALUE(val); } void *skb_push(struct sk_buff *skb, unsigned int len); diff --git a/mm/filemap.c b/mm/filemap.c index 1146fcfa3215..8a17f15806cc 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -2810,6 +2811,8 @@ static struct page *do_read_cache_page(struct address_space *mapping, page = wait_on_page_read(page); if (IS_ERR(page)) return page; + /* Assume all pages in page cache are initialized. */ + kmsan_unpoison_shadow(page_address(page), PAGE_SIZE); goto out; } if (PageUptodate(page)) diff --git a/mm/readahead.c b/mm/readahead.c index 2fe72cd29b47..1fe61128095b 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -193,7 +193,13 @@ unsigned int __do_page_cache_readahead(struct address_space *mapping, continue; } + /* + * The easiest way to handle these pages is to mark them + * untracked by KMSAN, assuming they're never used by anything + * else. + */ page = __page_cache_alloc(gfp_mask); + kmsan_ignore_page(page, /*order*/0); if (!page) break; page->index = page_offset; diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 03593eb240d8..5d88ed181422 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ EXPORT_SYMBOL(p9stat_free); size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size) { size_t len = min(pdu->size - pdu->offset, size); + kmsan_unpoison_shadow(&pdu->sdata[pdu->offset], len); memcpy(data, &pdu->sdata[pdu->offset], len); pdu->offset += len; return size - len; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index f57c610d7523..c3795e495196 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -12,6 +12,7 @@ #endif #include +#include /* for kmsan_unpoison_shadow() */ #include #include #include @@ -1054,6 +1055,12 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) err = -ENOMEM; goto failure; } + /* + * Unpoison the freshly created buffer to prevent KMSAN from reporting + * uninit errors. + * TODO(glider): unpoison it only when it's actually initialized. + */ + kmsan_unpoison_shadow(runtime->oss.buffer, runtime->oss.period_bytes); runtime->oss.params = 0; runtime->oss.prepare = 1;