Skip to content

Commit

Permalink
=> 2678: behlendorf/issue-2678 - Handle block pointers with a corrupt…
Browse files Browse the repository at this point in the history
… logical size

The general strategy used by ZFS to verify that blocks are is to
checksum everything.  This has the advantage of being extremely
robust and generically applicable regardless of the contents of
the block.  If a blocks checksum is valid then its contents are
trusted by the higher layers.

This system works exceptionally well as long bad data is never
written with a valid checksum.  However, if this does somehow
occur due to a software bug or a memory bit-flip on a non-ECC
system it may result in kernel panic.

One such place where this could occur is if somehow the logical
size stored in a block pointer exceeds the maximum block size.
This will result in an attempt to allocate a buffer greater than
the maximum block size causing a system panic.

To prevent this from happening the arc_read() function has been
updated to detect this specific case.  If a block pointer with an
invalid logical size is passed it will treat the block as if it
contained a checksum error.

Signed-off-by: Brian Behlendorf <[email protected]>
Issue openzfs#2678
  • Loading branch information
behlendorf authored and FransUrbo committed Oct 3, 2014
1 parent 46647a2 commit 7774961
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 7 deletions.
6 changes: 3 additions & 3 deletions include/sys/arc.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ typedef struct arc_buf_info {

void arc_space_consume(uint64_t space, arc_space_type_t type);
void arc_space_return(uint64_t space, arc_space_type_t type);
arc_buf_t *arc_buf_alloc(spa_t *spa, int size, void *tag,
arc_buf_t *arc_buf_alloc(spa_t *spa, uint64_t size, void *tag,
arc_buf_contents_t type);
arc_buf_t *arc_loan_buf(spa_t *spa, int size);
arc_buf_t *arc_loan_buf(spa_t *spa, uint64_t size);
void arc_return_buf(arc_buf_t *buf, void *tag);
void arc_loan_inuse_buf(arc_buf_t *buf, void *tag);
void arc_buf_add_ref(arc_buf_t *buf, void *tag);
boolean_t arc_buf_remove_ref(arc_buf_t *buf, void *tag);
void arc_buf_info(arc_buf_t *buf, arc_buf_info_t *abi, int state_index);
int arc_buf_size(arc_buf_t *buf);
uint64_t arc_buf_size(arc_buf_t *buf);
void arc_release(arc_buf_t *buf, void *tag);
int arc_released(arc_buf_t *buf);
void arc_buf_sigsegv(int sig, siginfo_t *si, void *unused);
Expand Down
14 changes: 10 additions & 4 deletions module/zfs/arc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1434,12 +1434,12 @@ arc_space_return(uint64_t space, arc_space_type_t type)
}

arc_buf_t *
arc_buf_alloc(spa_t *spa, int size, void *tag, arc_buf_contents_t type)
arc_buf_alloc(spa_t *spa, uint64_t size, void *tag, arc_buf_contents_t type)
{
arc_buf_hdr_t *hdr;
arc_buf_t *buf;

ASSERT3U(size, >, 0);
VERIFY3U(size, <=, SPA_MAXBLOCKSIZE);
hdr = kmem_cache_alloc(hdr_cache, KM_PUSHPAGE);
ASSERT(BUF_EMPTY(hdr));
hdr->b_size = size;
Expand Down Expand Up @@ -1477,7 +1477,7 @@ static char *arc_onloan_tag = "onloan";
* freed.
*/
arc_buf_t *
arc_loan_buf(spa_t *spa, int size)
arc_loan_buf(spa_t *spa, uint64_t size)
{
arc_buf_t *buf;

Expand Down Expand Up @@ -1837,7 +1837,7 @@ arc_buf_remove_ref(arc_buf_t *buf, void* tag)
return (no_callback);
}

int
uint64_t
arc_buf_size(arc_buf_t *buf)
{
return (buf->b_hdr->b_size);
Expand Down Expand Up @@ -3307,6 +3307,12 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, arc_done_func_t *done,
enum zio_compress b_compress = ZIO_COMPRESS_OFF;
uint64_t b_asize = 0;

/* Gracefully handle a damaged logical block size. */
if (size > SPA_MAXBLOCKSIZE) {
rc = ECKSUM;
goto out;
}

if (hdr == NULL) {
/* this block is not in the cache */
arc_buf_hdr_t *exists = NULL;
Expand Down

0 comments on commit 7774961

Please sign in to comment.