Subject: kern/32193: vop_strategy gets broken struct buf's passed by genfs/bread, possible memory leakage
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <reinoud@netbsd.org>
List: netbsd-bugs
Date: 11/29/2005 23:29:00
>Number: 32193
>Category: kern
>Synopsis: vop_strategy gets broken struct buf's passed by genfs/bread, possible memory leakage
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Tue Nov 29 23:29:00 +0000 2005
>Originator: Reinoud Zandijk
>Release: NetBSD 2.99.16/3.99.11
>Organization:
NetBSD
>Environment:
Kernel filingsystem development.
$NetBSD: vfs_bio.c,v 1.146 2005/06/09 02:19:59 atatat Exp $
$NetBSD: genfs_vnops.c,v 1.109 2005/11/12 22:29:53 yamt Exp $
Architecture: i386
Machine: i386
>Description:
When implementing the UDF filingsystem in the NetBSD kernel, i stumbled on
aparently some rare problems.
UDF's VOP_STRATEGY() gets calls from VOP_READ() using bread() on the vnode
and from genfs's {get,put}_pages. Both buffers are are not according to the
spec.
Problems might also occure in write equivalents though i've only studied
read functions.
VOP_STRATEGY buffers
--------------------
vop_strategy buffers are passed from genfs in
sys/miscfs/genfs/genfs_vnops.c:836's VOP_TRATEGY call and created at either
line 673 or at line 810 of the same file. In the buffer `mbp' created at
line 673, all seems OK but at the buffer `bp' created at line 810,
bp->b_bufsize is not initialised and thus ZERO!!!! quite a violation.
{
s = splbio();
bp = pool_get(&bufpool, PR_WAITOK);
splx(s);
BUF_INIT(bp);
bp->b_data = (char *)kva + offset - startoffset;
bp->b_resid = bp->b_bcount = iobytes;
bp->b_flags = B_BUSY|B_READ|B_CALL|B_ASYNC;
bp->b_iodone = uvm_aio_biodone1;
bp->b_vp = vp;
bp->b_proc = NULL;
};
This also brings the question as to where these buf `bp' that are only
created when the `mbp' needs to be fragmented are ever released... i can't
find it. `mbp' is released at line 864
s = splbio();
pool_put(&bufpool, mbp);
splx(s);
and the pages are mapped out in the following line:
uvm_pagermapout(kva, npages);
pages, the `bp' are still pointing at... Aparently this code is written so
that `mbp' can be waited for by `biowait' when all `bp' fragments are
ready. But the struct buf's claimed from genfs's own pool are not returned
to the pool. Each time this fragmented scheme is used, memory gets lost.
bread buffers
-------------
vop_strategy buffers are passed from bread() in sys/kern/vfs_bio.c's
bio_doread() at line 597's VOP_STRATEGY().
These buffers are claimed/looked up just before in line 577's getblk().
When passed to UDF's vop_strategy() bp->b_resid is undefined though mostly
ZERO. Also not according to the struct buf's specs wich would suggest the
number of bytes to be read/written in/from the buffer to be bp->b_resid.
Other filingsystems
----------
Filingsystems seem to cope with it by passing the buffers directly to the
device layer that aparently ignores most of the buf contents and only
reacts to bp->b_count.
Filingsystems that do care about the buffer contents are also only looking
at bp->b_count.
>How-To-Repeat:
Write a filingsystem :-/
>Fix:
Initialise bp->b_bufsize to iobytes in genfs_vnops.c around line 810
....
BUF_INIT(bp);
bp->b_data = (char *)kva + offset - startoffset;
bp->b_resid = bp->b_bcount = bp->b_bufsize = iobytes;
bp->b_flags = B_BUSY|B_READ|B_CALL|B_ASYNC;
bp->b_iodone = uvm_aio_biodone1;
bp->b_vp = vp;
bp->b_proc = NULL;
....
Initialise bp->b_resid to size in vfs_bio.c in bio_doread() and write
companion.
>Unformatted: