From: Hugh Dickins On some architectures, mapping the scatterlist may coalesce entries: if that coalesced list is then used for freeing the pages afterwards, there's a danger that pages may be doubly freed (and others leaked). Fix OnStream SCSI Tape's normalize_buffer by freeing from a second list beyond the I/O scatterlist, saving page pointers with their lengths. Signed-off-by: Hugh Dickins Cc: Douglas Gilbert Cc: James Bottomley Cc: Christoph Hellwig Cc: Willem Riede Warning: untested! Signed-off-by: Andrew Morton --- drivers/scsi/osst.c | 13 ++++++++++--- 1 files changed, 10 insertions(+), 3 deletions(-) diff -puN drivers/scsi/osst.c~osst-dont-doublefree-pages-from-scatterlist drivers/scsi/osst.c --- devel/drivers/scsi/osst.c~osst-dont-doublefree-pages-from-scatterlist 2006-02-07 13:19:08.000000000 -0800 +++ devel-akpm/drivers/scsi/osst.c 2006-02-07 13:19:08.000000000 -0800 @@ -5150,7 +5150,8 @@ static struct osst_buffer * new_tape_buf else priority = GFP_KERNEL; - i = sizeof(struct osst_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist); + i = sizeof(struct osst_buffer) + + (2 * osst_max_sg_segs - 1) * sizeof(struct scatterlist); tb = (struct osst_buffer *)kmalloc(i, priority); if (!tb) { printk(KERN_NOTICE "osst :I: Can't allocate new tape buffer.\n"); @@ -5225,6 +5226,8 @@ static int enlarge_buffer(struct osst_bu #if DEBUG STbuffer->buffer_size = got; #endif + memcpy(STbuffer->sg + STbuffer->sg_segs, STbuffer->sg, + STbuffer->sg_segs * sizeof(STbuffer->sg[0])); normalize_buffer(STbuffer); return 0; } @@ -5233,6 +5236,9 @@ static int enlarge_buffer(struct osst_bu STbuffer->buffer_size = got; STbuffer->sg_segs = ++segs; } + /* Save uncoalesced scatterlist of pages to be freed */ + memcpy(STbuffer->sg + STbuffer->sg_segs, STbuffer->sg, + STbuffer->sg_segs * sizeof(STbuffer->sg[0])); #if DEBUG if (debugging) { printk(OSST_DEB_MSG @@ -5252,9 +5258,10 @@ static int enlarge_buffer(struct osst_bu /* Release the segments */ static void normalize_buffer(struct osst_buffer *STbuffer) { - int i, order, b_size; + int i, order, b_size; - for (i=0; i < STbuffer->sg_segs; i++) { + /* Scatterlist entries may have been coalesced: free saved pagelist */ + for (i = STbuffer->sg_segs; i < 2*STbuffer->sg_segs; i++) { for (b_size = PAGE_SIZE, order = 0; b_size < STbuffer->sg[i].length; _