* [PATCH 0/3] block: add queue-private command filter, editable via sysfs @ 2012-09-12 11:25 Paolo Bonzini 2012-09-12 11:25 ` [PATCH 1/3] block: add back queue-private command filter Paolo Bonzini ` (2 more replies) 0 siblings, 3 replies; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 11:25 UTC (permalink / raw) To: linux-kernel Cc: Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi [sorry for the resend, I used a wrong mailing list address] The set of use cases for SG_IO is quite variable that no single filter can accomodate all of them. The current filter is tailored very much to CD burning, and includes many MMC-specific commands that may have other meanings in different standards. Someone may want to remove those commands; at the same time, people that trust their users may want to add persistent reservations, trim/discard, or even access to vendor-specific commands. Filters used to be mutable via sysfs, but the implementation was never enabled. Add it back, and let the admin set this up per device. The ideal is that we would be much more restrictive by default and give root the ability to override this both globally and per-device. But this piece of the policy should probably be implemented in userspace for better backwards compatibility. In the meanwhile, this patch series provides the sysfs knob. It is a tweaked revert of commit 018e044 (block: get rid of queue-private command filter, 2009-06-26). Paolo Bonzini (3): block: add back queue-private command filter scsi: create an all-zero filter for scanners block: add back command filter modification via sysfs Documentation/block/queue-sysfs.txt | 16 +++++ block/Kconfig | 10 +++ block/blk-sysfs.c | 43 +++++++++++++ block/bsg.c | 2 +- block/scsi_ioctl.c | 117 +++++++++++++++++++++++++++++++---- drivers/scsi/scsi_scan.c | 6 ++- drivers/scsi/sg.c | 7 +- include/linux/blkdev.h | 31 +++++++++- 8 files changed, 213 insertions(+), 19 deletions(-) ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/3] block: add back queue-private command filter 2012-09-12 11:25 [PATCH 0/3] block: add queue-private command filter, editable via sysfs Paolo Bonzini @ 2012-09-12 11:25 ` Paolo Bonzini 2012-09-12 11:25 ` [PATCH 2/3] scsi: create an all-zero filter for scanners Paolo Bonzini 2012-09-12 11:25 ` [PATCH 3/3] block: add back command filter modification via sysfs Paolo Bonzini 2 siblings, 0 replies; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 11:25 UTC (permalink / raw) To: linux-kernel Cc: Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi The command filter used to be mutable via sysfs, but this was broken and backed out. Let's add it back. This patch adds the infrastructure for filtering, but unlike the old code this one just adds a pointer to request_queue, so as to make it cheaper in the majority of cases where no special filtering is desired. This is a partial (and massaged) revert of commit 018e044 (block: get rid of queue-private command filter, 2009-06-26). Cc: linux-scsi@vger.kernel.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- block/blk-sysfs.c | 2 ++ block/bsg.c | 2 +- block/scsi_ioctl.c | 17 +++++------------ drivers/scsi/sg.c | 4 +++- include/linux/blkdev.h | 10 +++++++++- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 9628b29..5a0de07 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -487,6 +487,8 @@ static void blk_release_queue(struct kobject *kobj) blkcg_exit_queue(q); + kfree(q->cmd_filter); + if (q->elevator) { spin_lock_irq(q->queue_lock); ioc_clear_queue(q); diff --git a/block/bsg.c b/block/bsg.c index ff64ae3..09956da 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -187,7 +187,7 @@ static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, return -EFAULT; if (hdr->subprotocol == BSG_SUB_PROTOCOL_SCSI_CMD) { - if (blk_verify_command(rq->cmd, has_write_perm)) + if (blk_verify_command(q->cmd_filter, rq->cmd, has_write_perm)) return -EPERM; } else if (!capable(CAP_SYS_RAWIO)) return -EPERM; diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 9a87daa..c8862e9 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -33,11 +33,6 @@ #include <scsi/scsi_ioctl.h> #include <scsi/scsi_cmnd.h> -struct blk_cmd_filter { - unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; - unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; -}; - static struct blk_cmd_filter blk_default_cmd_filter; /* Command group 3 is reserved and should never be used. */ @@ -196,17 +191,15 @@ static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) __set_bit(GPCMD_SET_READ_AHEAD, filter->write_ok); } -int blk_verify_command(unsigned char *cmd, fmode_t has_write_perm) +int blk_verify_command(struct blk_cmd_filter *filter, + unsigned char *cmd, fmode_t has_write_perm) { - struct blk_cmd_filter *filter = &blk_default_cmd_filter; - /* root can do any command. */ if (capable(CAP_SYS_RAWIO)) return 0; - /* if there's no filter set, assume we're filtering everything out */ if (!filter) - return -EPERM; + filter = &blk_default_cmd_filter; /* Anybody who can open the device can do a read-safe command */ if (test_bit(cmd[0], filter->read_ok)) @@ -225,7 +218,7 @@ static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, { if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len)) return -EFAULT; - if (blk_verify_command(rq->cmd, mode & FMODE_WRITE)) + if (blk_verify_command(q->cmd_filter, rq->cmd, mode & FMODE_WRITE)) return -EPERM; /* @@ -472,7 +465,7 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode, if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) goto error; - err = blk_verify_command(rq->cmd, mode & FMODE_WRITE); + err = blk_verify_command(q->cmd_filter, rq->cmd, mode & FMODE_WRITE); if (err) goto error; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9c5c5f2..2ba7c82 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -217,11 +217,13 @@ static void sg_put_dev(Sg_device *sdp); static int sg_allow_access(struct file *filp, unsigned char *cmd) { struct sg_fd *sfp = filp->private_data; + struct request_queue *q = sfp->parentdp->device->request_queue; if (sfp->parentdp->device->type == TYPE_SCANNER) return 0; - return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); + return blk_verify_command(q->cmd_filter, + cmd, filp->f_mode & FMODE_WRITE); } static int get_exclude(Sg_device *sdp) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4a2ab7c..b5c5f8a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -258,6 +258,11 @@ struct blk_queue_tag { #define BLK_SCSI_MAX_CMDS (256) #define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) +struct blk_cmd_filter { + unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; + unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; +}; + struct queue_limits { unsigned long bounce_pfn; unsigned long seg_boundary_mask; @@ -423,6 +428,8 @@ struct request_queue { struct bsg_class_device bsg_dev; #endif + struct blk_cmd_filter *cmd_filter; + #ifdef CONFIG_BLK_CGROUP struct list_head all_q_node; #endif @@ -1005,7 +1012,8 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, gfp_mask); } -extern int blk_verify_command(unsigned char *cmd, fmode_t has_write_perm); +extern int blk_verify_command(struct blk_cmd_filter *filter, + unsigned char *cmd, fmode_t has_write_perm); enum blk_default_limits { BLK_MAX_SEGMENTS = 128, -- 1.7.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/3] scsi: create an all-zero filter for scanners 2012-09-12 11:25 [PATCH 0/3] block: add queue-private command filter, editable via sysfs Paolo Bonzini 2012-09-12 11:25 ` [PATCH 1/3] block: add back queue-private command filter Paolo Bonzini @ 2012-09-12 11:25 ` Paolo Bonzini 2012-09-12 11:25 ` [PATCH 3/3] block: add back command filter modification via sysfs Paolo Bonzini 2 siblings, 0 replies; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 11:25 UTC (permalink / raw) To: linux-kernel Cc: Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi Using /dev/sg for scanners is blocked from unprivileged users. Reimplement this using customizable command filters, so that the sysfs knobs will work in this case too. Cc: linux-scsi@vger.kernel.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- drivers/scsi/scsi_scan.c | 6 +++++- drivers/scsi/sg.c | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 56a9379..d168c15 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -773,13 +773,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, } switch (sdev->type) { + case TYPE_SCANNER: + sdev->request_queue->cmd_filter = + kzalloc(sizeof(struct blk_cmd_filter), GFP_KERNEL); + /* fallthrough */ + case TYPE_RBC: case TYPE_TAPE: case TYPE_DISK: case TYPE_PRINTER: case TYPE_MOD: case TYPE_PROCESSOR: - case TYPE_SCANNER: case TYPE_MEDIUM_CHANGER: case TYPE_ENCLOSURE: case TYPE_COMM: diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 2ba7c82..c7474f5 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -219,9 +219,6 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) struct sg_fd *sfp = filp->private_data; struct request_queue *q = sfp->parentdp->device->request_queue; - if (sfp->parentdp->device->type == TYPE_SCANNER) - return 0; - return blk_verify_command(q->cmd_filter, cmd, filp->f_mode & FMODE_WRITE); } -- 1.7.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/3] block: add back command filter modification via sysfs 2012-09-12 11:25 [PATCH 0/3] block: add queue-private command filter, editable via sysfs Paolo Bonzini 2012-09-12 11:25 ` [PATCH 1/3] block: add back queue-private command filter Paolo Bonzini 2012-09-12 11:25 ` [PATCH 2/3] scsi: create an all-zero filter for scanners Paolo Bonzini @ 2012-09-12 11:25 ` Paolo Bonzini 2012-09-12 12:38 ` Alan Cox 2012-09-12 12:41 ` Alan Cox 2 siblings, 2 replies; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 11:25 UTC (permalink / raw) To: linux-kernel Cc: Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi This adds two new sysfs attributes to the queue kobject. The attributes allow reading and writing the whitelist of unprivileged commands. This is again a bit different from what was removed in commit 018e044 (block: get rid of queue-private command filter, 2009-06-26), but the idea is the same. One difference is that it does not use a separate kobject. Also, the supported sysfs syntax is a bit more expressive: it includes ranges, the ability to replace all of the filter with a single command, and does not force usage of hexadecimal. Since the names are different, and the old ones were anyway never really enabled, the different API is not a problem. Cc: linux-scsi@vger.kernel.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- Documentation/block/queue-sysfs.txt | 16 ++++++ block/Kconfig | 10 ++++ block/blk-sysfs.c | 41 ++++++++++++++ block/scsi_ioctl.c | 100 +++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 21 +++++++ 5 files changed, 188 insertions(+), 0 deletions(-) diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt index e54ac1d..1152b38 100644 --- a/Documentation/block/queue-sysfs.txt +++ b/Documentation/block/queue-sysfs.txt @@ -133,6 +133,22 @@ control of this block device to that new IO scheduler. Note that writing an IO scheduler name to this file will attempt to load that IO scheduler module, if it isn't already present in the system. +sgio_read_filter (RW) +--------------------- +When read, this file will display a list of SCSI commands (i.e. values of +the first byte of a CDB) that are always available for unprivileged users +(via /dev/bsg, /dev/sgNN, or ioctls such as SG_IO and CDROM_SEND_PACKET). +When written, the list of commands will be modified. By default it +will be completely replaced; writing a string that begins with '+' will +add new commands, and writing a string that begins with '-' will remove +some commands. Ranges of commands are supported, for example '0x00-0xff'. + +sgio_write_filter (RW) +---------------------- +When read, this file will display a list of SCSI commands (i.e. values of +the first byte of a CDB) that are available for unprivileged users +when the block device is open for writing. Writing to this file behaves +as for sgio_read_filter. Jens Axboe <jens.axboe@oracle.com>, February 2009 diff --git a/block/Kconfig b/block/Kconfig index 09acf1b..7c369d9 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -65,6 +65,16 @@ config BLK_DEV_BSG If unsure, say Y. +config BLK_DEV_SG_FILTER_SYSFS + bool "Customizable SG_IO filters in sysfs" + default y + help + Saying Y here will let you use sysfs to customize the list + of SCSI commands that are available (via /dev/sg, /dev/bsg or + ioctls such as SG_IO) to unprivileged users. + + If unsure, say Y. + config BLK_DEV_BSGLIB bool "Block layer SG support v4 helper lib" default n diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 5a0de07..b287d62 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -394,6 +394,43 @@ static struct queue_sysfs_entry queue_random_entry = { .store = queue_store_random, }; +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +static ssize_t queue_sgio_filter_read_show(struct request_queue *q, char *page) +{ + return blk_filter_show(q, page, READ); +} + +static ssize_t queue_sgio_filter_write_show(struct request_queue *q, + char *page) +{ + return blk_filter_show(q, page, WRITE); +} + +static ssize_t queue_sgio_filter_read_store(struct request_queue *q, + const char *page, size_t count) +{ + return blk_filter_store(q, page, count, READ); +} + +static ssize_t queue_sgio_filter_write_store(struct request_queue *q, + const char *page, size_t count) +{ + return blk_filter_store(q, page, count, WRITE); +} + +static struct queue_sysfs_entry queue_sgio_filter_read_entry = { + .attr = { .name = "sgio_filter_read", .mode = S_IRUGO | S_IWUSR }, + .show = queue_sgio_filter_read_show, + .store = queue_sgio_filter_read_store, +}; + +static struct queue_sysfs_entry queue_sgio_filter_write_entry = { + .attr = {.name = "sgio_filter_write", .mode = S_IRUGO | S_IWUSR }, + .show = queue_sgio_filter_write_show, + .store = queue_sgio_filter_write_store, +}; +#endif + static struct attribute *default_attrs[] = { &queue_requests_entry.attr, &queue_ra_entry.attr, @@ -416,6 +453,10 @@ static struct attribute *default_attrs[] = { &queue_rq_affinity_entry.attr, &queue_iostats_entry.attr, &queue_random_entry.attr, +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS + &queue_sgio_filter_read_entry.attr, + &queue_sgio_filter_write_entry.attr, +#endif NULL, }; diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index c8862e9..61a43a6 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -18,6 +18,7 @@ */ #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/ctype.h> #include <linux/string.h> #include <linux/module.h> #include <linux/blkdev.h> @@ -110,6 +111,8 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) { + memset(filter, 0, sizeof(*filter)); + /* Basic read-only commands */ __set_bit(TEST_UNIT_READY, filter->read_ok); __set_bit(REQUEST_SENSE, filter->read_ok); @@ -738,6 +741,103 @@ int scsi_cmd_blk_ioctl(struct block_device *bd, fmode_t mode, } EXPORT_SYMBOL(scsi_cmd_blk_ioctl); +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +ssize_t blk_filter_show(struct request_queue *q, char *page, int rw) +{ + struct blk_cmd_filter *filter; + char *p = page; + unsigned long *okbits; + int i; + + filter = q->cmd_filter; + if (!filter) + filter = &blk_default_cmd_filter; + + if (rw == READ) + okbits = filter->read_ok; + else + okbits = filter->write_ok; + + for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) + if (test_bit(i, okbits)) + p += sprintf(p, "0x%02x ", i); + + if (p > page) + p[-1] = '\n'; + + return p - page; +} +EXPORT_SYMBOL_GPL(blk_filter_show); + +ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw) +{ + unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits; + bool set; + const char *p = page; + char *endp; + int start = -1, cmd; + + if (!q->cmd_filter) { + q->cmd_filter = kmalloc(sizeof(struct blk_cmd_filter), + GFP_KERNEL); + blk_set_cmd_filter_defaults(q->cmd_filter); + } + + if (rw == READ) + target_okbits = q->cmd_filter->read_ok; + else + target_okbits = q->cmd_filter->write_ok; + + set = (p[0] != '-'); + if (p[0] != '-' && p[0] != '+') + memset(okbits, 0, sizeof(okbits)); + else { + memcpy(okbits, target_okbits, sizeof(okbits)); + p++; + } + + while (p < page + count) { + if (start == -1 && isspace(*p)) { + p++; + continue; + } + + cmd = simple_strtol(p, &endp, 0); + + /* all of these cases means invalid input, so do nothing. */ + if (endp == p || cmd < 0 || cmd >= BLK_SCSI_MAX_CMDS || + (start != -1 && endp < page + count && !isspace(*endp))) + return -EINVAL; + + p = endp; + if (p < page + count && *p == '-') { + BUG_ON(start != -1); + start = cmd; + p++; + continue; + } + + if (start == -1) + start = cmd; + + for (; start <= cmd; start++) + if (set) + __set_bit(start, okbits); + else + __clear_bit(start, okbits); + start = -1; + } + + if (start != -1) + return -EINVAL; + + memcpy(target_okbits, okbits, sizeof(okbits)); + return count; +} +EXPORT_SYMBOL_GPL(blk_filter_store); +#endif + static int __init blk_scsi_ioctl_init(void) { blk_set_cmd_filter_defaults(&blk_default_cmd_filter); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b5c5f8a..18e7c25 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1012,9 +1012,30 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, gfp_mask); } +/* + * command filter functions + */ extern int blk_verify_command(struct blk_cmd_filter *filter, unsigned char *cmd, fmode_t has_write_perm); +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +ssize_t blk_filter_show(struct request_queue *q, char *page, int rw); +ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw); +#else +static inline ssize_t blk_filter_show(struct request_queue *q, char *page, int rw) +{ + return -EINVAL; +} + +static inline ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw); +{ + return -EINVAL; +} +#endif /* CONFIG_BLK_DEV_SG_FILTER_SYSFS */ + + enum blk_default_limits { BLK_MAX_SEGMENTS = 128, BLK_SAFE_MAX_SECTORS = 255, -- 1.7.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] block: add back command filter modification via sysfs 2012-09-12 11:25 ` [PATCH 3/3] block: add back command filter modification via sysfs Paolo Bonzini @ 2012-09-12 12:38 ` Alan Cox 2012-09-12 12:41 ` Alan Cox 1 sibling, 0 replies; 12+ messages in thread From: Alan Cox @ 2012-09-12 12:38 UTC (permalink / raw) To: Paolo Bonzini Cc: linux-kernel, Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi O > + if (!q->cmd_filter) { > + q->cmd_filter = kmalloc(sizeof(struct blk_cmd_filter), > + GFP_KERNEL); > + blk_set_cmd_filter_defaults(q->cmd_filter); Out of memory - memset - oops ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] block: add back command filter modification via sysfs 2012-09-12 11:25 ` [PATCH 3/3] block: add back command filter modification via sysfs Paolo Bonzini 2012-09-12 12:38 ` Alan Cox @ 2012-09-12 12:41 ` Alan Cox 2012-09-12 12:56 ` Paolo Bonzini 1 sibling, 1 reply; 12+ messages in thread From: Alan Cox @ 2012-09-12 12:41 UTC (permalink / raw) To: Paolo Bonzini Cc: linux-kernel, Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi > +ssize_t blk_filter_store(struct request_queue *q, > + const char *page, size_t count, int rw) > +{ > + unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits; > + bool set; > + const char *p = page; > + char *endp; > + int start = -1, cmd; > + > + if (!q->cmd_filter) { > + q->cmd_filter = kmalloc(sizeof(struct blk_cmd_filter), > + GFP_KERNEL); > + blk_set_cmd_filter_defaults(q->cmd_filter); > + } > + This also needs CAP_SYS_RAWIO otherwise you have a capability escalation path. I'm not really in favour of this patch as is. It's not as flexible as doing it with a BPF filter which if we are going to have a new API is going to be cleaner, faster and has a clear understood API plus tools. With BPF you can do things like enabling command A with option B on a specific device for a certain block range. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] block: add back command filter modification via sysfs 2012-09-12 12:41 ` Alan Cox @ 2012-09-12 12:56 ` Paolo Bonzini 0 siblings, 0 replies; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 12:56 UTC (permalink / raw) To: Alan Cox Cc: linux-kernel, Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-scsi Il 12/09/2012 14:41, Alan Cox ha scritto: >> > + if (!q->cmd_filter) { >> > + q->cmd_filter = kmalloc(sizeof(struct blk_cmd_filter), >> > + GFP_KERNEL); >> > + blk_set_cmd_filter_defaults(q->cmd_filter); >> > + } >> > + > This also needs CAP_SYS_RAWIO otherwise you have a capability escalation > path. Seems more like for CAP_SYS_ADMIN, since it can affect other processes than the one writing to the file. > I'm not really in favour of this patch as is. It's not as flexible as > doing it with a BPF filter which if we are going to have a new API is > going to be cleaner, faster and has a clear understood API plus tools. > > With BPF you can do things like enabling command A with option B on a > specific device for a certain block range. I liked the BPF idea, either with SCM_RIGHTS or cgroups, but I do wonder if it's overengineered. There are two uses for the filtering: - non-privileged users who want to burn a CD or something like that. For this, a bitmap is more than enough. Customizing the bitmap lets userspace fix the case of different meanings for the same byte value in different SCSI standards. - virtualization who wants to pass through almost everything, but still run as confined as possible. In this case a more complex filtering can be done just as easily in userspace (i.e. QEMU). Of course this means the filter can be subverted if the guest can escape the QEMU jail, but the "almost everything" takes care of that, for example you could still block WRITE BUFFER commands. I would be okay with a ioctl to disable the filter altogether (four options: regular filter, no filter, no SG_IO at all, choose depending on CAP_SYS_RAWIO). Then you can use file descriptor passing with SCM_RIGHTS, and do everything in userspace. But it doesn't work too well with the first usage above, besides being a larger patch. Paolo ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 0/3] SG_IO command filtering via sysfs @ 2018-11-10 16:35 Paolo Bonzini 2018-11-10 16:35 ` [PATCH 3/3] block: add back command filter modification " Paolo Bonzini 0 siblings, 1 reply; 12+ messages in thread From: Paolo Bonzini @ 2018-11-10 16:35 UTC (permalink / raw) To: linux-kernel Cc: linux-scsi, Hannes Reinecke, Martin K. Petersen, James Bottomley Currently, SG_IO ioctls are implemented so that non-CAP_SYS_RAWIO users can send commands from a predetermined whitelist. The whitelist is very simple-minded though, and basically corresponds to MMC commands---the idea being that it would be nice for local users to read/copy/burn CDs. This was probably sensible when the whitelist was first added (in the pre-git era), but quite a few things have changed since then: - there is a lot more focus on not running things as root unnecessarily; it is generally much more common to have non-root processes accessing disks and we would like that to happen more, not less. - there is also a lot more focus on not giving capabilities unnecessarily. Using CAP_SYS_RAWIO, which gives full access to all commands, allows you to send a WRITE SCSI command to a file opened for reading, which is a nice recipe for data corruption. A more fine-grained whitelist allows you to give the desired access to the application. - we've discovered that some commands conflict between the various SCSI standards. UNMAP (a write command) in SBC has the same number as the obscure MMC command READ SUBCHANNEL. As such it's allowed if a block device is opened for reading! This series, which was last sent in 2012 before I lost interest in the endless discussions that followed, adds the possibility to make the filter mutable via sysfs, so that it can be set up per device. This of course can go both ways; interested applications can set a wider filter, but one can also imagine setting much more restrictive filters by default (possibly allowing little more than INQUIRY, TEST UNIT READY, READ CAPACITY and the like). Back then there was opposition to giving unfettered access to "dangerous" or "too easily destructive" commands such as WRITE SAME or PERSISTENT RESERVE OUT to unprivileged users. Even then, I think this objection is now moot thanks to the following things that have happened in 2012: - WRITE SAME commands, which were considered too destructive, have been added to the filter since commit 25cdb6451064 ("block: allow WRITE_SAME commands with the SG_IO ioctl", 2016-12-15, Linux 4.10). They are basically the only non-MMC commands included in the filter, by the way. - persistent reservations are also allowed now via PR ioctls (commit 924d55b06347, "sd: implement the Persistent Reservation API", 2015-10-21, Linux 4.4). These require CAP_SYS_ADMIN, which is the same capability that is needed to *grant* access to PR commands via the SG_IO filter. So, here is the 2018 version of these patches. Please review! :) Paolo Paolo Bonzini (3): block: add back queue-private command filter scsi: create an all-one filter for scanners block: add back command filter modification via sysfs Documentation/block/queue-sysfs.txt | 19 +++++ block/Kconfig | 10 +++ block/blk-sysfs.c | 43 ++++++++++++ block/bsg-lib.c | 4 +- block/bsg.c | 8 +-- block/scsi_ioctl.c | 136 +++++++++++++++++++++++++++++++++--- drivers/scsi/scsi_scan.c | 13 ++++ drivers/scsi/sg.c | 6 +- include/linux/blkdev.h | 18 ++++- include/linux/bsg.h | 4 +- 10 files changed, 238 insertions(+), 23 deletions(-) -- 1.8.3.1 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/3] block: add back command filter modification via sysfs 2018-11-10 16:35 [PATCH 0/3] SG_IO command filtering " Paolo Bonzini @ 2018-11-10 16:35 ` Paolo Bonzini 2018-11-16 5:46 ` Bart Van Assche 0 siblings, 1 reply; 12+ messages in thread From: Paolo Bonzini @ 2018-11-10 16:35 UTC (permalink / raw) To: linux-kernel Cc: linux-scsi, Hannes Reinecke, Martin K. Petersen, James Bottomley This adds two new sysfs attributes to the queue kobject. The attributes allow reading and writing the whitelist of unprivileged commands. This is again a bit different from what was removed in commit 018e044 (block: get rid of queue-private command filter, 2009-06-26), but the idea is the same. One difference is that it does not use a separate kobject. Also, the supported sysfs syntax is a bit more expressive: it includes ranges, the ability to replace all of the filter with a single command, and does not force usage of hexadecimal. Since the names are different, and the old ones were anyway never really enabled, the different userspace API is not a problem. Cc: linux-scsi@vger.kernel.org Cc: Hannes Reinecke <hare@suse.com> Cc: Martin K. Petersen <martin.petersen@oracle.com> Cc: James Bottomley <James.Bottomley@hansenpartnership.com> Suggested-by: James Bottomley <James.Bottomley@HansenPartnership.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- Documentation/block/queue-sysfs.txt | 19 ++++++ block/Kconfig | 10 +++ block/blk-sysfs.c | 41 +++++++++++++ block/scsi_ioctl.c | 117 ++++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 9 +++ 5 files changed, 196 insertions(+) diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt index 2c1e67058fd3..1fe5fe5fd80a 100644 --- a/Documentation/block/queue-sysfs.txt +++ b/Documentation/block/queue-sysfs.txt @@ -162,6 +162,25 @@ control of this block device to that new IO scheduler. Note that writing an IO scheduler name to this file will attempt to load that IO scheduler module, if it isn't already present in the system. +sgio_read_filter (RW) +--------------------- +When read, this file will display a list of SCSI commands (i.e. values of +the first byte of a CDB) that are always available for unprivileged users +via /dev/bsg, /dev/sgNN, or ioctls such as SG_IO and CDROM_SEND_PACKET. +When written, the list of commands will be modified. The default filter +can be restored by writing "default" to the file; otherwise the input should +be a list of byte values or ranges such as "0x00-0xff". In the latter case, +instead of replacing the filter completely you can add to the commands, +by writing a string that begins with '+', or remove them by writing a +string that begins with '-'. + +sgio_write_filter (RW) +---------------------- +When read, this file will display a list of SCSI commands (i.e. values of +the first byte of a CDB) that are available for unprivileged users +when the block device is open for writing. Writing to this file behaves +as for sgio_read_filter. + write_cache (RW) ---------------- When read, this file will display whether the device has write back diff --git a/block/Kconfig b/block/Kconfig index 1f2469a0123c..a5bc37d50e07 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -71,6 +71,16 @@ config BLK_DEV_BSG access device serial numbers, etc. If unsure, say Y. + +config BLK_DEV_SG_FILTER_SYSFS + bool "Customizable SG_IO filters in sysfs" + default y + help + Saying Y here will let you use sysfs to customize the list + of SCSI commands that are available (via /dev/sg, /dev/bsg or + ioctls such as SG_IO) to unprivileged users. + + If unsure, say Y. config BLK_DEV_BSGLIB bool "Block layer SG support v4 helper lib" diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index d1ec150a3478..ea2a47272bcf 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -703,6 +703,43 @@ static ssize_t queue_dax_show(struct request_queue *q, char *page) }; #endif +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +static ssize_t queue_sgio_filter_read_show(struct request_queue *q, char *page) +{ + return blk_filter_show(q, page, READ); +} + +static ssize_t queue_sgio_filter_write_show(struct request_queue *q, + char *page) +{ + return blk_filter_show(q, page, WRITE); +} + +static ssize_t queue_sgio_filter_read_store(struct request_queue *q, + const char *page, size_t count) +{ + return blk_filter_store(q, page, count, READ); +} + +static ssize_t queue_sgio_filter_write_store(struct request_queue *q, + const char *page, size_t count) +{ + return blk_filter_store(q, page, count, WRITE); +} + +static struct queue_sysfs_entry queue_sgio_filter_read_entry = { + .attr = { .name = "sgio_filter_read", .mode = S_IRUGO | S_IWUSR }, + .show = queue_sgio_filter_read_show, + .store = queue_sgio_filter_read_store, +}; + +static struct queue_sysfs_entry queue_sgio_filter_write_entry = { + .attr = {.name = "sgio_filter_write", .mode = S_IRUGO | S_IWUSR }, + .show = queue_sgio_filter_write_show, + .store = queue_sgio_filter_write_store, +}; +#endif + static struct attribute *default_attrs[] = { &queue_requests_entry.attr, &queue_ra_entry.attr, @@ -740,6 +777,10 @@ static ssize_t queue_dax_show(struct request_queue *q, char *page) #ifdef CONFIG_BLK_DEV_THROTTLING_LOW &throtl_sample_time_entry.attr, #endif +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS + &queue_sgio_filter_read_entry.attr, + &queue_sgio_filter_write_entry.attr, +#endif NULL, }; diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 5d577c89f9e6..c5f089e7d869 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> +#include <linux/ctype.h> #include <linux/module.h> #include <linux/blkdev.h> #include <linux/capability.h> @@ -725,6 +726,125 @@ void scsi_req_init(struct scsi_request *req) } EXPORT_SYMBOL(scsi_req_init); + +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +ssize_t blk_filter_show(struct request_queue *q, char *page, int rw) +{ + struct blk_cmd_filter *filter; + char *p = page; + unsigned long *okbits; + int i; + + filter = q->cmd_filter; + if (!filter) + filter = &blk_default_cmd_filter; + + if (rw == READ) + okbits = filter->read_ok; + else + okbits = filter->write_ok; + + for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) + if (test_bit(i, okbits)) + p += sprintf(p, "0x%02x ", i); + + if (p > page) + p[-1] = '\n'; + + return p - page; +} +EXPORT_SYMBOL_GPL(blk_filter_show); + +ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw) +{ + unsigned long bits[BLK_SCSI_CMD_PER_LONG], *target_bits; + bool set; + const char *p = page; + char *endp; + int start = -1, cmd; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (sysfs_streq(p, "default")) { + if (!q->cmd_filter) + return 0; + if (rw == READ) + memcpy(q->cmd_filter->read_ok, + blk_default_cmd_filter.read_ok, + sizeof(blk_default_cmd_filter.read_ok)); + else + memcpy(q->cmd_filter->write_ok, + blk_default_cmd_filter.write_ok, + sizeof(blk_default_cmd_filter.write_ok)); + return count; + } + + if (!q->cmd_filter) { + q->cmd_filter = kmemdup(&blk_default_cmd_filter, + sizeof(struct blk_cmd_filter), + GFP_KERNEL); + if (!q->cmd_filter) + return -ENOMEM; + } + + if (rw == READ) + target_bits = q->cmd_filter->read_ok; + else + target_bits = q->cmd_filter->write_ok; + + set = (p[0] != '-'); + if (p[0] != '-' && p[0] != '+') + memset(bits, 0, sizeof(bits)); + else { + memcpy(bits, target_bits, sizeof(bits)); + p++; + } + + while (p < page + count) { + if (start == -1 && isspace(*p)) { + p++; + continue; + } + + cmd = simple_strtol(p, &endp, 0); + if (endp == p || cmd < 0 || cmd >= BLK_SCSI_MAX_CMDS) + return -EINVAL; + + /* Verify the character immediately after the number, if any */ + p = endp; + if (p < page + count) { + if (start == -1 && *p == '-') { + start = cmd; + p++; + continue; + } + if (!isspace(*p)) + return -EINVAL; + } + + if (start == -1) + start = cmd; + + for (; start <= cmd; start++) + if (set) + __set_bit(start, bits); + else + __clear_bit(start, bits); + start = -1; + } + + /* Did the string end with a dash? */ + if (start != -1) + return -EINVAL; + + memcpy(target_bits, bits, sizeof(bits)); + return count; +} +EXPORT_SYMBOL_GPL(blk_filter_store); +#endif + static int __init blk_scsi_ioctl_init(void) { blk_set_cmd_filter_defaults(&blk_default_cmd_filter); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index df46a36c9467..5110cd6d0d16 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1430,9 +1430,18 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, gfp_mask, 0); } +/* + * Command filter functions + */ extern int blk_verify_command(struct request_queue *q, unsigned char *cmd, fmode_t mode); +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +ssize_t blk_filter_show(struct request_queue *q, char *page, int rw); +ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw); +#endif /* CONFIG_BLK_DEV_SG_FILTER_SYSFS */ + enum blk_default_limits { BLK_MAX_SEGMENTS = 128, BLK_SAFE_MAX_SECTORS = 255, -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] block: add back command filter modification via sysfs 2018-11-10 16:35 ` [PATCH 3/3] block: add back command filter modification " Paolo Bonzini @ 2018-11-16 5:46 ` Bart Van Assche 2018-11-16 7:00 ` Paolo Bonzini 0 siblings, 1 reply; 12+ messages in thread From: Bart Van Assche @ 2018-11-16 5:46 UTC (permalink / raw) To: Paolo Bonzini, linux-kernel Cc: linux-scsi, Hannes Reinecke, Martin K. Petersen, James Bottomley On Sat, 2018-11-10 at 17:35 +0100, Paolo Bonzini wrote: > +sgio_read_filter (RW) > +--------------------- > +When read, this file will display a list of SCSI commands (i.e. values of > +the first byte of a CDB) that are always available for unprivileged users > +via /dev/bsg, /dev/sgNN, or ioctls such as SG_IO and CDROM_SEND_PACKET. > +When written, the list of commands will be modified. The default filter > +can be restored by writing "default" to the file; otherwise the input should > +be a list of byte values or ranges such as "0x00-0xff". In the latter case, > +instead of replacing the filter completely you can add to the commands, > +by writing a string that begins with '+', or remove them by writing a > +string that begins with '-'. > + > +sgio_write_filter (RW) > +---------------------- > +When read, this file will display a list of SCSI commands (i.e. values of > +the first byte of a CDB) that are available for unprivileged users > +when the block device is open for writing. Writing to this file behaves > +as for sgio_read_filter. This seems like an unfortunate API choice to me. Let's have a look at the following SBC commands: * READ(6); opcode 08h. * READ(10); opcode 28h. * READ(12); opcode A8h. * READ(16); opcode 88h. * READ(32); opcode 7fh; service action 0009h. I do not know any application for which it would be useful to allow some but not all of these commands. With the proposed interface however users will have to examine all SCSI opcodes and for each opcode they will have to decide whether or not it should be allowed. Additionally, for opcodes like 7fh that represent multiple commands, users will have to decide whether they want to allow all these commands or none. That's why I think that filtering SCSI commands based on their CDB is an unfortunate choice. Would it be sufficient for the use cases you are looking at to group SCSI commands as follows and to enable/disable these commands per group: * SCSI command that read information from the medium (e.g. READ) or from the controller (e.g. READ CAPACITY). * SCSI commands that modify information on the medium (e.g. WRITE). * SCSI commands that modify controller settings (e.g. MODE SELECT or SET TARGET PORT GROUPS). Bart. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] block: add back command filter modification via sysfs 2018-11-16 5:46 ` Bart Van Assche @ 2018-11-16 7:00 ` Paolo Bonzini 2018-11-16 14:42 ` Bart Van Assche 0 siblings, 1 reply; 12+ messages in thread From: Paolo Bonzini @ 2018-11-16 7:00 UTC (permalink / raw) To: Bart Van Assche, linux-kernel Cc: linux-scsi, Hannes Reinecke, Martin K. Petersen, James Bottomley On 16/11/18 06:46, Bart Van Assche wrote: > I do not know any application for which it would be useful to allow some but > not all of these commands. With the proposed interface however users will > have to examine all SCSI opcodes and for each opcode they will have to decide > whether or not it should be allowed. Additionally, for opcodes like 7fh that > represent multiple commands, users will have to decide whether they want to > allow all these commands or none. That's why I think that filtering SCSI > commands based on their CDB is an unfortunate choice. Would it be sufficient > for the use cases you are looking at to group SCSI commands as follows and to > enable/disable these commands per group: > * SCSI command that read information from the medium (e.g. READ) or from the > controller (e.g. READ CAPACITY). > * SCSI commands that modify information on the medium (e.g. WRITE). > * SCSI commands that modify controller settings (e.g. MODE SELECT or SET > TARGET PORT GROUPS). And also: * all SCSI commands (e.g. write microcode, vendor specific commands). It would. However, it would be impossible to do this without making the filter depend on the SCSI device type. This has been rejected in 2012. Paolo ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] block: add back command filter modification via sysfs 2018-11-16 7:00 ` Paolo Bonzini @ 2018-11-16 14:42 ` Bart Van Assche 0 siblings, 0 replies; 12+ messages in thread From: Bart Van Assche @ 2018-11-16 14:42 UTC (permalink / raw) To: Paolo Bonzini, linux-kernel Cc: linux-scsi, Hannes Reinecke, Martin K. Petersen, James Bottomley On Fri, 2018-11-16 at 08:00 +0100, Paolo Bonzini wrote: > On 16/11/18 06:46, Bart Van Assche wrote: > > I do not know any application for which it would be useful to allow some but > > not all of these commands. With the proposed interface however users will > > have to examine all SCSI opcodes and for each opcode they will have to decide > > whether or not it should be allowed. Additionally, for opcodes like 7fh that > > represent multiple commands, users will have to decide whether they want to > > allow all these commands or none. That's why I think that filtering SCSI > > commands based on their CDB is an unfortunate choice. Would it be sufficient > > for the use cases you are looking at to group SCSI commands as follows and to > > enable/disable these commands per group: > > * SCSI command that read information from the medium (e.g. READ) or from the > > controller (e.g. READ CAPACITY). > > * SCSI commands that modify information on the medium (e.g. WRITE). > > * SCSI commands that modify controller settings (e.g. MODE SELECT or SET > > TARGET PORT GROUPS). > > And also: > > * all SCSI commands (e.g. write microcode, vendor specific commands). > > It would. However, it would be impossible to do this without making the > filter depend on the SCSI device type. This has been rejected in 2012. Do you have a link available to that discussion? Since the meaning of several SCSI opcodes depends on the SCSI device type, I don't see how we can avoid making filtering dependent on the SCSI device type. Bart. ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 0/3] block: add queue-private command filter, editable via sysfs @ 2012-09-12 11:23 Paolo Bonzini 2012-09-12 11:23 ` [PATCH 3/3] block: add back command filter modification " Paolo Bonzini 0 siblings, 1 reply; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 11:23 UTC (permalink / raw) To: linux-kernel Cc: Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-iscsi The set of use cases for SG_IO is quite variable that no single filter can accomodate all of them. The current filter is tailored very much to CD burning, and includes many MMC-specific commands that may have other meanings in different standards. Someone may want to remove those commands; at the same time, people that trust their users may want to add persistent reservations, trim/discard, or even access to vendor-specific commands. Filters used to be mutable via sysfs, but the implementation was never enabled. Add it back, and let the admin set this up per device. The ideal is that we would be much more restrictive by default and give root the ability to override this both globally and per-device. But this piece of the policy should probably be implemented in userspace for better backwards compatibility. In the meanwhile, this patch series provides the sysfs knob. It is a tweaked revert of commit 018e044 (block: get rid of queue-private command filter, 2009-06-26). Paolo Bonzini (3): block: add back queue-private command filter scsi: create an all-zero filter for scanners block: add back command filter modification via sysfs Documentation/block/queue-sysfs.txt | 16 +++++ block/Kconfig | 10 +++ block/blk-sysfs.c | 43 +++++++++++++ block/bsg.c | 2 +- block/scsi_ioctl.c | 117 +++++++++++++++++++++++++++++++---- drivers/scsi/scsi_scan.c | 6 ++- drivers/scsi/sg.c | 7 +- include/linux/blkdev.h | 31 +++++++++- 8 files changed, 213 insertions(+), 19 deletions(-) ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/3] block: add back command filter modification via sysfs 2012-09-12 11:23 [PATCH 0/3] block: add queue-private command filter, editable " Paolo Bonzini @ 2012-09-12 11:23 ` Paolo Bonzini 0 siblings, 0 replies; 12+ messages in thread From: Paolo Bonzini @ 2012-09-12 11:23 UTC (permalink / raw) To: linux-kernel Cc: Tejun Heo, James Bottomley, Jens Axboe, Ric Wheeler, linux-iscsi This adds two new sysfs attributes to the queue kobject. The attributes allow reading and writing the whitelist of unprivileged commands. This is again a bit different from what was removed in commit 018e044 (block: get rid of queue-private command filter, 2009-06-26), but the idea is the same. One difference is that it does not use a separate kobject. Also, the supported sysfs syntax is a bit more expressive: it includes ranges, the ability to replace all of the filter with a single command, and does not force usage of hexadecimal. Since the names are different, and the old ones were anyway never really enabled, the different API is not a problem. Cc: linux-iscsi@vger.kernel.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- Documentation/block/queue-sysfs.txt | 16 ++++++ block/Kconfig | 10 ++++ block/blk-sysfs.c | 41 ++++++++++++++ block/scsi_ioctl.c | 100 +++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 21 +++++++ 5 files changed, 188 insertions(+), 0 deletions(-) diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt index e54ac1d..1152b38 100644 --- a/Documentation/block/queue-sysfs.txt +++ b/Documentation/block/queue-sysfs.txt @@ -133,6 +133,22 @@ control of this block device to that new IO scheduler. Note that writing an IO scheduler name to this file will attempt to load that IO scheduler module, if it isn't already present in the system. +sgio_read_filter (RW) +--------------------- +When read, this file will display a list of SCSI commands (i.e. values of +the first byte of a CDB) that are always available for unprivileged users +(via /dev/bsg, /dev/sgNN, or ioctls such as SG_IO and CDROM_SEND_PACKET). +When written, the list of commands will be modified. By default it +will be completely replaced; writing a string that begins with '+' will +add new commands, and writing a string that begins with '-' will remove +some commands. Ranges of commands are supported, for example '0x00-0xff'. + +sgio_write_filter (RW) +---------------------- +When read, this file will display a list of SCSI commands (i.e. values of +the first byte of a CDB) that are available for unprivileged users +when the block device is open for writing. Writing to this file behaves +as for sgio_read_filter. Jens Axboe <jens.axboe@oracle.com>, February 2009 diff --git a/block/Kconfig b/block/Kconfig index 09acf1b..7c369d9 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -65,6 +65,16 @@ config BLK_DEV_BSG If unsure, say Y. +config BLK_DEV_SG_FILTER_SYSFS + bool "Customizable SG_IO filters in sysfs" + default y + help + Saying Y here will let you use sysfs to customize the list + of SCSI commands that are available (via /dev/sg, /dev/bsg or + ioctls such as SG_IO) to unprivileged users. + + If unsure, say Y. + config BLK_DEV_BSGLIB bool "Block layer SG support v4 helper lib" default n diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 5a0de07..b287d62 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -394,6 +394,43 @@ static struct queue_sysfs_entry queue_random_entry = { .store = queue_store_random, }; +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +static ssize_t queue_sgio_filter_read_show(struct request_queue *q, char *page) +{ + return blk_filter_show(q, page, READ); +} + +static ssize_t queue_sgio_filter_write_show(struct request_queue *q, + char *page) +{ + return blk_filter_show(q, page, WRITE); +} + +static ssize_t queue_sgio_filter_read_store(struct request_queue *q, + const char *page, size_t count) +{ + return blk_filter_store(q, page, count, READ); +} + +static ssize_t queue_sgio_filter_write_store(struct request_queue *q, + const char *page, size_t count) +{ + return blk_filter_store(q, page, count, WRITE); +} + +static struct queue_sysfs_entry queue_sgio_filter_read_entry = { + .attr = { .name = "sgio_filter_read", .mode = S_IRUGO | S_IWUSR }, + .show = queue_sgio_filter_read_show, + .store = queue_sgio_filter_read_store, +}; + +static struct queue_sysfs_entry queue_sgio_filter_write_entry = { + .attr = {.name = "sgio_filter_write", .mode = S_IRUGO | S_IWUSR }, + .show = queue_sgio_filter_write_show, + .store = queue_sgio_filter_write_store, +}; +#endif + static struct attribute *default_attrs[] = { &queue_requests_entry.attr, &queue_ra_entry.attr, @@ -416,6 +453,10 @@ static struct attribute *default_attrs[] = { &queue_rq_affinity_entry.attr, &queue_iostats_entry.attr, &queue_random_entry.attr, +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS + &queue_sgio_filter_read_entry.attr, + &queue_sgio_filter_write_entry.attr, +#endif NULL, }; diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index c8862e9..61a43a6 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -18,6 +18,7 @@ */ #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/ctype.h> #include <linux/string.h> #include <linux/module.h> #include <linux/blkdev.h> @@ -110,6 +111,8 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) static void blk_set_cmd_filter_defaults(struct blk_cmd_filter *filter) { + memset(filter, 0, sizeof(*filter)); + /* Basic read-only commands */ __set_bit(TEST_UNIT_READY, filter->read_ok); __set_bit(REQUEST_SENSE, filter->read_ok); @@ -738,6 +741,103 @@ int scsi_cmd_blk_ioctl(struct block_device *bd, fmode_t mode, } EXPORT_SYMBOL(scsi_cmd_blk_ioctl); +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +ssize_t blk_filter_show(struct request_queue *q, char *page, int rw) +{ + struct blk_cmd_filter *filter; + char *p = page; + unsigned long *okbits; + int i; + + filter = q->cmd_filter; + if (!filter) + filter = &blk_default_cmd_filter; + + if (rw == READ) + okbits = filter->read_ok; + else + okbits = filter->write_ok; + + for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) + if (test_bit(i, okbits)) + p += sprintf(p, "0x%02x ", i); + + if (p > page) + p[-1] = '\n'; + + return p - page; +} +EXPORT_SYMBOL_GPL(blk_filter_show); + +ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw) +{ + unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits; + bool set; + const char *p = page; + char *endp; + int start = -1, cmd; + + if (!q->cmd_filter) { + q->cmd_filter = kmalloc(sizeof(struct blk_cmd_filter), + GFP_KERNEL); + blk_set_cmd_filter_defaults(q->cmd_filter); + } + + if (rw == READ) + target_okbits = q->cmd_filter->read_ok; + else + target_okbits = q->cmd_filter->write_ok; + + set = (p[0] != '-'); + if (p[0] != '-' && p[0] != '+') + memset(okbits, 0, sizeof(okbits)); + else { + memcpy(okbits, target_okbits, sizeof(okbits)); + p++; + } + + while (p < page + count) { + if (start == -1 && isspace(*p)) { + p++; + continue; + } + + cmd = simple_strtol(p, &endp, 0); + + /* all of these cases means invalid input, so do nothing. */ + if (endp == p || cmd < 0 || cmd >= BLK_SCSI_MAX_CMDS || + (start != -1 && endp < page + count && !isspace(*endp))) + return -EINVAL; + + p = endp; + if (p < page + count && *p == '-') { + BUG_ON(start != -1); + start = cmd; + p++; + continue; + } + + if (start == -1) + start = cmd; + + for (; start <= cmd; start++) + if (set) + __set_bit(start, okbits); + else + __clear_bit(start, okbits); + start = -1; + } + + if (start != -1) + return -EINVAL; + + memcpy(target_okbits, okbits, sizeof(okbits)); + return count; +} +EXPORT_SYMBOL_GPL(blk_filter_store); +#endif + static int __init blk_scsi_ioctl_init(void) { blk_set_cmd_filter_defaults(&blk_default_cmd_filter); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index b5c5f8a..18e7c25 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1012,9 +1012,30 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block, gfp_mask); } +/* + * command filter functions + */ extern int blk_verify_command(struct blk_cmd_filter *filter, unsigned char *cmd, fmode_t has_write_perm); +#ifdef CONFIG_BLK_DEV_SG_FILTER_SYSFS +ssize_t blk_filter_show(struct request_queue *q, char *page, int rw); +ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw); +#else +static inline ssize_t blk_filter_show(struct request_queue *q, char *page, int rw) +{ + return -EINVAL; +} + +static inline ssize_t blk_filter_store(struct request_queue *q, + const char *page, size_t count, int rw); +{ + return -EINVAL; +} +#endif /* CONFIG_BLK_DEV_SG_FILTER_SYSFS */ + + enum blk_default_limits { BLK_MAX_SEGMENTS = 128, BLK_SAFE_MAX_SECTORS = 255, -- 1.7.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2018-11-16 14:42 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-09-12 11:25 [PATCH 0/3] block: add queue-private command filter, editable via sysfs Paolo Bonzini 2012-09-12 11:25 ` [PATCH 1/3] block: add back queue-private command filter Paolo Bonzini 2012-09-12 11:25 ` [PATCH 2/3] scsi: create an all-zero filter for scanners Paolo Bonzini 2012-09-12 11:25 ` [PATCH 3/3] block: add back command filter modification via sysfs Paolo Bonzini 2012-09-12 12:38 ` Alan Cox 2012-09-12 12:41 ` Alan Cox 2012-09-12 12:56 ` Paolo Bonzini -- strict thread matches above, loose matches on Subject: below -- 2018-11-10 16:35 [PATCH 0/3] SG_IO command filtering " Paolo Bonzini 2018-11-10 16:35 ` [PATCH 3/3] block: add back command filter modification " Paolo Bonzini 2018-11-16 5:46 ` Bart Van Assche 2018-11-16 7:00 ` Paolo Bonzini 2018-11-16 14:42 ` Bart Van Assche 2012-09-12 11:23 [PATCH 0/3] block: add queue-private command filter, editable " Paolo Bonzini 2012-09-12 11:23 ` [PATCH 3/3] block: add back command filter modification " Paolo Bonzini
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).