]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/isofs/rock.c
[PATCH] uclinux: use PER_LINUX_32BIT in binfmt_flat
[linux-2.6-omap-h63xx.git] / fs / isofs / rock.c
index 977dd7009c071f68532dc24fca7a5a76c62e7622..4326cb47f8fa267667311d2b65b361aa66508858 100644 (file)
@@ -81,9 +81,22 @@ static void init_rock_state(struct rock_state *rs, struct inode *inode)
 static int rock_continue(struct rock_state *rs)
 {
        int ret = 1;
+       int blocksize = 1 << rs->inode->i_blkbits;
+       const int min_de_size = offsetof(struct rock_ridge, u);
 
        kfree(rs->buffer);
        rs->buffer = NULL;
+
+       if ((unsigned)rs->cont_offset > blocksize - min_de_size ||
+           (unsigned)rs->cont_size > blocksize ||
+           (unsigned)(rs->cont_offset + rs->cont_size) > blocksize) {
+               printk(KERN_NOTICE "rock: corrupted directory entry. "
+                       "extent=%d, offset=%d, size=%d\n",
+                       rs->cont_extent, rs->cont_offset, rs->cont_size);
+               ret = -EIO;
+               goto out;
+       }
+
        if (rs->cont_extent) {
                struct buffer_head *bh;
 
@@ -113,6 +126,66 @@ out:
        return ret;
 }
 
+/*
+ * We think there's a record of type `sig' at rs->chr.  Parse the signature
+ * and make sure that there's really room for a record of that type.
+ */
+static int rock_check_overflow(struct rock_state *rs, int sig)
+{
+       int len;
+
+       switch (sig) {
+       case SIG('S', 'P'):
+               len = sizeof(struct SU_SP_s);
+               break;
+       case SIG('C', 'E'):
+               len = sizeof(struct SU_CE_s);
+               break;
+       case SIG('E', 'R'):
+               len = sizeof(struct SU_ER_s);
+               break;
+       case SIG('R', 'R'):
+               len = sizeof(struct RR_RR_s);
+               break;
+       case SIG('P', 'X'):
+               len = sizeof(struct RR_PX_s);
+               break;
+       case SIG('P', 'N'):
+               len = sizeof(struct RR_PN_s);
+               break;
+       case SIG('S', 'L'):
+               len = sizeof(struct RR_SL_s);
+               break;
+       case SIG('N', 'M'):
+               len = sizeof(struct RR_NM_s);
+               break;
+       case SIG('C', 'L'):
+               len = sizeof(struct RR_CL_s);
+               break;
+       case SIG('P', 'L'):
+               len = sizeof(struct RR_PL_s);
+               break;
+       case SIG('T', 'F'):
+               len = sizeof(struct RR_TF_s);
+               break;
+       case SIG('Z', 'F'):
+               len = sizeof(struct RR_ZF_s);
+               break;
+       default:
+               len = 0;
+               break;
+       }
+       len += offsetof(struct rock_ridge, u);
+       if (len > rs->len) {
+               printk(KERN_NOTICE "rock: directory entry would overflow "
+                               "storage\n");
+               printk(KERN_NOTICE "rock: sig=0x%02x, size=%d, remaining=%d\n",
+                               sig, len, rs->len);
+               return -EIO;
+       }
+       return 0;
+}
+
 /*
  * return length of name field; 0: not found, -1: to be ignored
  */
@@ -139,10 +212,12 @@ repeat:
                if (rr->len < 3)
                        goto out;       /* Something got screwed up here */
                sig = isonum_721(rs.chr);
+               if (rock_check_overflow(&rs, sig))
+                       goto eio;
                rs.chr += rr->len;
                rs.len -= rr->len;
                if (rs.len < 0)
-                       goto out;       /* corrupted isofs */
+                       goto eio;       /* corrupted isofs */
 
                switch (sig) {
                case SIG('R', 'R'):
@@ -200,6 +275,9 @@ repeat:
 out:
        kfree(rs.buffer);
        return ret;
+eio:
+       ret = -EIO;
+       goto out;
 }
 
 static int
@@ -232,10 +310,12 @@ repeat:
                if (rr->len < 3)
                        goto out;       /* Something got screwed up here */
                sig = isonum_721(rs.chr);
+               if (rock_check_overflow(&rs, sig))
+                       goto eio;
                rs.chr += rr->len;
                rs.len -= rr->len;
                if (rs.len < 0)
-                       goto out;       /* corrupted isofs */
+                       goto eio;       /* corrupted isofs */
 
                switch (sig) {
 #ifndef CONFIG_ZISOFS          /* No flag for SF or ZF */
@@ -466,6 +546,9 @@ repeat:
 out:
        kfree(rs.buffer);
        return ret;
+eio:
+       ret = -EIO;
+       goto out;
 }
 
 static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
@@ -605,6 +688,8 @@ repeat:
                if (rr->len < 3)
                        goto out;       /* Something got screwed up here */
                sig = isonum_721(rs.chr);
+               if (rock_check_overflow(&rs, sig))
+                       goto out;
                rs.chr += rr->len;
                rs.len -= rr->len;
                if (rs.len < 0)