xorl %eax, %eax

CVE-2008-5700: Linux kernel LibATA SG_IO Time-out

with 2 comments

This vulnerability was reported by Linus Torvalds on 5 December 2008 and affects Linux kernel prior to 2.6.27.9 as you can read from its ChangeLog. This bug was present at the device driver for SG v4 interface. The function responsible for filling the SG v4 header with the block layer request is located at block/bsg.c and here is this function:

174 static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq,
175                                 struct sg_io_v4 *hdr, struct bsg_device *bd,
176                                 int has_write_perm)
177 {
178        if (hdr->request_len > BLK_MAX_CDB) {
        ...
184        if (copy_from_user(rq->cmd, (void *)(unsigned long)hdr->request,
185                           hdr->request_len))
        ...
200        rq->timeout = (hdr->timeout * HZ) / 1000;
201        if (!rq->timeout)
202                rq->timeout = q->sg_timeout;
203        if (!rq->timeout)
204                rq->timeout = BLK_DEFAULT_SG_TIMEOUT;
205
206        return 0;
207 }

At line 200, the time-out of the request is initialized to the header’s time-out * HZ / 1000. If request’s time-out value is 0 (line 201), it updates it with the time-out of the SG request queue’s one. If this is also 0 (line 203), it sets it to BLK_DEFAULT_SG_TIMEOUT which is a constant defined in include/linux/blkdev.h like this:

622 /*
623  * default timeout for SG_IO if none specified
624  */
625 #define BLK_DEFAULT_SG_TIMEOUT  (60 * HZ)

Linus Torvalds noticed that having time-out values that are that short could result in several seconds of waiting if the command eventually times out, since it will move into the reset sequence which requires some time to abort the command. His exact conclusion was:

As a result, shorter timeouts than a few seconds simply do not make
sense, as the recovery would be longer than the timeout itself.

To do this, they added another constant value at the latter header file which had a shorter time-out value:

 #define BLK_DEFAULT_SG_TIMEOUT  (60 * HZ)
+#define BLK_MIN_SG_TIMEOUT      (7 * HZ)

And the the time-out initialization of the function at block/bsg.c was updated to include an additional option for time-outs shorter than the new BLK_MIN_SG_TIMEOUT value:

         rq->timeout = BLK_DEFAULT_SG_TIMEOUT;
+    if (rq->timeout < BLK_MIN_SG_TIMEOUT)
+        rq->timeout = BLK_MIN_SG_TIMEOUT;

The above code could be reached easily through block/scsi_ioctl.c where blk_fill_sghdr_rq() was using the exact same approach like this:

191 static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq,
192                              struct sg_io_hdr *hdr, struct file *file)
193 {
194        if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len))
195                return -EFAULT;
196        if (blk_verify_command(&q->cmd_filter, rq->cmd,
197                               file->f_mode & FMODE_WRITE))
198                return -EPERM;
199
200        /*
201         * fill in request structure
202         */
203        rq->cmd_len = hdr->cmd_len;
204        rq->cmd_type = REQ_TYPE_BLOCK_PC;
205
206        rq->timeout = msecs_to_jiffies(hdr->timeout);
207        if (!rq->timeout)
208                rq->timeout = q->sg_timeout;
209        if (!rq->timeout)
210                rq->timeout = BLK_DEFAULT_SG_TIMEOUT;
211
212        return 0;
213 }

Which is this called by sg_io() routine from the same source code file:

262 static int sg_io(struct file *file, struct request_queue *q,
263                 struct gendisk *bd_disk, struct sg_io_hdr *hdr) 
264 {
       ...
295        if (blk_fill_sghdr_rq(q, rq, hdr, file)) {
296                blk_put_request(rq);
297                return -EFAULT;
298        }
       ...
345 }

Which is at last, could be invoked directly through the following IOCTL routine:

523 int scsi_cmd_ioctl(struct file *file, struct request_queue *q,
524                    struct gendisk *bd_disk, unsigned int cmd, void __user *arg)
525 {
      ...
531        switch (cmd) {
532                /*
533                 * new sgv3 interface
534                 */ 
      ...
559                case SG_IO: {
560                        struct sg_io_hdr hdr;
561
562                        err = -EFAULT;
563                        if (copy_from_user(&hdr, arg, sizeof(hdr)))
564                                break;
565                        err = sg_io(file, q, bd_disk, &hdr);
566                        if (err == -EFAULT)
567                                break;
568
569                        if (copy_to_user(arg, &hdr, sizeof(hdr)))
570                                err = -EFAULT;
571                        break;
572                }
      ...
651 }


To fix this, the patch was also applied in blk_fill_sghdr_rq() function like this:

         rq->timeout = BLK_DEFAULT_SG_TIMEOUT;
+    if (rq->timeout < BLK_MIN_SG_TIMEOUT)
+        rq->timeout = BLK_MIN_SG_TIMEOUT;

Eugene Teo also wrote some interesting comments about that bug.

Written by xorl

May 6, 2009 at 13:08

Posted in bugs, linux

2 Responses

Subscribe to comments with RSS.

  1. Will you turn on RSS/Atom for your blog entries? Thanks.

    Anon

    May 6, 2009 at 16:42

  2. RSS feeds are enabled. Check out your browser (there should be something like that RSS icon that firefox has at the address bar or something), else subscribe directly to https://xorl.wordpress.com/feed/ using your favorite software.

    xorl

    May 6, 2009 at 16:58


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s