[Bug 228807] dump can core
bugzilla-noreply at freebsd.org
bugzilla-noreply at freebsd.org
Mon Jun 11 16:38:02 UTC 2018
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=228807
--- Comment #2 from Diane Bruce <db at FreeBSD.org> ---
> > + sblock = NULL;
> > if ((ret = sbget(diskfd, &sblock, -1)) != 0) {
> > switch (ret) {
> > case ENOENT:
This is all completely bogus as mentioned by @emaste. I know
exactly the bug now. Here are the excruciating details. (Well most of it)
Setting sblock to NULL is not needed here because of the way sbget works.
Heinsenberg was playing here.
dump main.c starts up and works out sizes of various things
looking at line 454 and 455 We see the following snippet
...
maxino = sblock->fs_ipg * sblock->fs_ncg;
mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE);
...
Looking at my SSD and using printfs in main.c we see
sblock->fs_ipg= 80256 sblock->fs_ncg = 655 maxino=52567680
mapsize = 6571008
inodes are dumped to "tape" from dumpino() line 459 in traverse.c
dumpmap is then called line 832 traverse.c
On my SSD spcl.c_count is worked out in and this results in a spcl.c_count=6417
Eventually flushtape() line 227 in tape.c is called
line 296 of tape.c we have
...
if (spcl.c_type != TS_END) {
for (i = 0; i < spcl.c_count; i++)
if (spcl.c_addr[i] != 0)
blks++;
}
...
spcl.c_count of 6147 is quite a bit out of bounds for spcl.c_addr
and it segfaults here.
In fact, looking at /usr/include/protocols/dumprestore.h
line 117
...
int32_t c_count; /* number of valid c_addr entries
*/
char c_addr[TP_NINDIR]; /* 1 => data; 0 => hole in inode */
char c_label[LBLSIZE]; /* dump label */
...
Looking at TP_NINDIR comment at line 53 of dumprestore.h
...
* TP_NINDIR is the number of indirect pointers in a TS_INODE
* or TS_ADDR record. Note that it must be a power of two.
*/
#define TP_BSIZE 1024
#define NTREC 10
#define HIGHDENSITYTREC 32
#define TP_NINDIR (TP_BSIZE/2)
...
Hence c_addr[TP_NINDIR]; is 512 bytes. We overflow this fairly easily.
&spcl = 0x2100d8
top = 0x2104d8
&pcl.c_addr[3715]=0x210fff
(lldb) x/x 0x210ffe
0x00210ffe: 0x00000000
warning: Not all bytes (2/4) were able to be read from 0x210ffe.
Boom.
Apparently when the sblock buffer was static, the linked arrangment
of sblock_buf[MAXBSIZE] and spcl from dumprestore.h was "just right"
to mask this bounds error. This would also explain why malloc'ing
a huge area for the superblock made no difference.
I don't know how to fix this. You will note that a dump on my spinning
rust system will also go out of bounds in c_addr but perhaps the
following variables are not needed at this point? e.g. c_label and up.
It's also overflowing spcl itself and going into memory it shouldn't.
I'd not trust the dump itself at this point.
...
sblock->fs_ipg= 80256 sblock->fs_ncg = 319 maxino=25601664
mapsize = 3201024
...
spcl=0x2100d8
top= 0x2104d8
spcl.c_count=3126
spcl.c_addr[3125]=0 blks=266
spcl.c_addr[3125]=0 blks=267
&spcl.c_addr[3125]=0x210db1
^^ it doesn't segfault here because it doesn't get to a page boundary
but it certainly is outside the bounds of spcl.
This was fun!
--
You are receiving this mail because:
You are the assignee for the bug.
More information about the freebsd-bugs
mailing list