X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fisofs%2Frock.c;h=6bd48f0a7047d7560f06492dfe57e53141be6a17;hb=95dfec6ae1cb8c03406aac612a5642cbddb676b3;hp=efefbcce4ce958e2865c824dcc2fe34f88b69cab;hpb=ba40aaf04314ec5efd090e69518033fc55f450fa;p=linux-2.6 diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index efefbcce4c..6bd48f0a70 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -13,12 +13,14 @@ #include "isofs.h" #include "rock.h" -/* These functions are designed to read the system areas of a directory record +/* + * These functions are designed to read the system areas of a directory record * and extract relevant information. There are different functions provided * depending upon what information we need at the time. One function fills * out an inode structure, a second one extracts a filename, a third one * returns a symbolic link name, and a fourth one returns the extent number - * for the file. */ + * for the file. + */ #define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */ @@ -34,7 +36,7 @@ struct rock_state { /* * This is a way of ensuring that we have something in the system - * use fields that is compatible with Rock Ridge. Return zero on success. + * use fields that is compatible with Rock Ridge. Return zero on success. */ static int check_sp(struct rock_ridge *rr, struct inode *inode) @@ -79,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; @@ -111,7 +126,69 @@ out: return ret; } -/* return length of name field; 0: not found, -1: to be ignored */ +/* + * 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 + */ int get_rock_ridge_filename(struct iso_directory_record *de, char *retname, struct inode *inode) { @@ -135,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'): @@ -196,6 +275,9 @@ repeat: out: kfree(rs.buffer); return ret; +eio: + ret = -EIO; + goto out; } static int @@ -228,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 */ @@ -390,8 +474,10 @@ repeat: isofs_iget(inode->i_sb, ISOFS_I(inode)->i_first_extent, 0); - if (!reloc) + if (IS_ERR(reloc)) { + ret = PTR_ERR(reloc); goto out; + } inode->i_mode = reloc->i_mode; inode->i_nlink = reloc->i_nlink; inode->i_uid = reloc->i_uid; @@ -462,6 +548,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) @@ -535,8 +624,11 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) { int result = parse_rock_ridge_inode_internal(de, inode, 0); - /* if rockridge flag was reset and we didn't look for attributes - * behind eventual XA attributes, have a look there */ + + /* + * if rockridge flag was reset and we didn't look for attributes + * behind eventual XA attributes, have a look there + */ if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1) && (ISOFS_SB(inode->i_sb)->s_rock == 2)) { result = parse_rock_ridge_inode_internal(de, inode, 14); @@ -544,9 +636,10 @@ int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) return result; } -/* readpage() for symlinks: reads symlink contents into the page and either - makes it uptodate and returns 0 or returns error (-EIO) */ - +/* + * readpage() for symlinks: reads symlink contents into the page and either + * makes it uptodate and returns 0 or returns error (-EIO) + */ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; @@ -584,8 +677,10 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) if (offset + *pnt > bufsize) goto out_bad_span; - /* Now test for possible Rock Ridge extensions which will override - some of these numbers in the inode structure. */ + /* + * Now test for possible Rock Ridge extensions which will override + * some of these numbers in the inode structure. + */ setup_rock_ridge(raw_de, inode, &rs); @@ -595,6 +690,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) @@ -659,6 +756,6 @@ error: return -EIO; } -struct address_space_operations isofs_symlink_aops = { +const struct address_space_operations isofs_symlink_aops = { .readpage = rock_ridge_symlink_readpage };