Index: head/sbin/savecore/savecore.c =================================================================== --- head/sbin/savecore/savecore.c +++ head/sbin/savecore/savecore.c @@ -436,7 +436,7 @@ { xo_handle_t *xostdout, *xoinfo; static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX]; - static char *buf = NULL; + static char *buf = NULL, *temp = NULL; struct kerneldumpheader kdhf, kdhl; off_t mediasize, dumpsize, firsthd, lasthd; FILE *info, *fp; @@ -490,14 +490,29 @@ printf("sectorsize = %u\n", sectorsize); } + if (sectorsize < sizeof(kdhl)) { + syslog(LOG_ERR, + "Sector size is less the kernel dump header %zu", + sizeof(kdhl)); + goto closefd; + } + lasthd = mediasize - sectorsize; + if (temp == NULL) { + temp = malloc(sectorsize); + if (temp == NULL) { + syslog(LOG_ERR, "%m"); + return; + } + } if (lseek(fd, lasthd, SEEK_SET) != lasthd || - read(fd, &kdhl, sizeof(kdhl)) != sizeof(kdhl)) { + read(fd, temp, sectorsize) != sectorsize) { syslog(LOG_ERR, "error reading last dump header at offset %lld in %s: %m", (long long)lasthd, device); goto closefd; } + memcpy(&kdhl, temp, sizeof(kdhl)); istextdump = 0; if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) { if (verbose) @@ -567,15 +582,16 @@ goto closefd; } dumpsize = dtoh64(kdhl.dumplength); - firsthd = lasthd - dumpsize - sizeof kdhf; + firsthd = lasthd - dumpsize - sectorsize; if (lseek(fd, firsthd, SEEK_SET) != firsthd || - read(fd, &kdhf, sizeof(kdhf)) != sizeof(kdhf)) { + read(fd, temp, sectorsize) != sectorsize) { syslog(LOG_ERR, "error reading first dump header at offset %lld in %s: %m", (long long)firsthd, device); nerr++; goto closefd; } + memcpy(&kdhf, temp, sizeof(kdhf)); if (verbose >= 2) { printf("First dump headers:\n"); @@ -586,7 +602,7 @@ printf("\n"); } - if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { + if (memcmp(&kdhl, &kdhf, sizeof(kdhl))) { syslog(LOG_ERR, "first and last dump headers disagree on %s", device); nerr++; @@ -603,7 +619,7 @@ exit(0); } - if (kdhl.panicstring[0]) + if (kdhl.panicstring[0] != '\0') syslog(LOG_ALERT, "reboot after panic: %*s", (int)sizeof(kdhl.panicstring), kdhl.panicstring); else @@ -724,9 +740,10 @@ if (!keep) { if (verbose) printf("clearing dump header\n"); - memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic); + memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof(kdhl.magic)); + memcpy(temp, &kdhl, sizeof(kdhl)); if (lseek(fd, lasthd, SEEK_SET) != lasthd || - write(fd, &kdhl, sizeof(kdhl)) != sizeof(kdhl)) + write(fd, temp, sectorsize) != sectorsize) syslog(LOG_ERR, "error while clearing the dump header: %m"); } Index: head/sys/amd64/amd64/minidump_machdep.c =================================================================== --- head/sys/amd64/amd64/minidump_machdep.c +++ head/sys/amd64/amd64/minidump_machdep.c @@ -56,9 +56,6 @@ */ #define SIZEOF_METADATA (64*1024) -#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) -#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) - uint64_t *vm_page_dump; int vm_page_dump_size; @@ -222,6 +219,7 @@ int error; uint64_t bits; uint64_t *pml4, *pdp, *pd, *pt, pa; + size_t size; int i, ii, j, k, n, bit; int retry_count; struct minidumphdr mdhdr; @@ -319,12 +317,12 @@ dumpsize += PAGE_SIZE; /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) { error = E2BIG; goto fail; } dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; + dumplo -= di->blocksize * 2; progress = dumpsize; /* Initialize mdhdr */ @@ -344,10 +342,10 @@ ptoa((uintmax_t)physmem) / 1048576); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += size; /* Dump my header */ bzero(&fakepd, sizeof(fakepd)); @@ -432,10 +430,10 @@ goto fail; /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += size; /* Signal completion, signoff and exit stage left. */ dump_write(di, NULL, 0, 0, 0); Index: head/sys/kern/kern_dump.c =================================================================== --- head/sys/kern/kern_dump.c +++ head/sys/kern/kern_dump.c @@ -55,13 +55,11 @@ */ #define SIZEOF_METADATA (64*1024) -#define MD_ALIGN(x) (((off_t)(x) + PAGE_MASK) & ~PAGE_MASK) -#define DEV_ALIGN(x) (((off_t)(x) + (DEV_BSIZE-1)) & ~(DEV_BSIZE-1)) +#define MD_ALIGN(x) roundup2((off_t)(x), PAGE_SIZE) off_t dumplo; /* Handle buffered writes. */ -static char buffer[DEV_BSIZE]; static size_t fragsz; struct dump_pa dump_map[DUMPSYS_MD_PA_NPAIRS]; @@ -125,19 +123,19 @@ int error; while (sz) { - len = DEV_BSIZE - fragsz; + len = di->blocksize - fragsz; if (len > sz) len = sz; - bcopy(ptr, buffer + fragsz, len); + memcpy((char *)di->blockbuf + fragsz, ptr, len); fragsz += len; ptr += len; sz -= len; - if (fragsz == DEV_BSIZE) { - error = dump_write(di, buffer, 0, dumplo, - DEV_BSIZE); + if (fragsz == di->blocksize) { + error = dump_write(di, di->blockbuf, 0, dumplo, + di->blocksize); if (error) return (error); - dumplo += DEV_BSIZE; + dumplo += di->blocksize; fragsz = 0; } } @@ -152,8 +150,8 @@ if (fragsz == 0) return (0); - error = dump_write(di, buffer, 0, dumplo, DEV_BSIZE); - dumplo += DEV_BSIZE; + error = dump_write(di, di->blockbuf, 0, dumplo, di->blocksize); + dumplo += di->blocksize; fragsz = 0; return (error); } @@ -286,7 +284,7 @@ Elf_Ehdr ehdr; uint64_t dumpsize; off_t hdrgap; - size_t hdrsz; + size_t hdrsz, size; int error; #ifndef __powerpc__ @@ -324,15 +322,15 @@ hdrsz = ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize; fileofs = MD_ALIGN(hdrsz); dumpsize += fileofs; - hdrgap = fileofs - DEV_ALIGN(hdrsz); + hdrgap = fileofs - roundup2((off_t)hdrsz, di->blocksize); /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + if (di->mediasize < SIZEOF_METADATA + dumpsize + di->blocksize * 2) { error = ENOSPC; goto fail; } dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; + dumplo -= di->blocksize * 2; mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARCH_VERSION, dumpsize, di->blocksize); @@ -341,10 +339,10 @@ ehdr.e_phnum - DUMPSYS_NUM_AUX_HDRS); /* Dump leader */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); if (error) goto fail; - dumplo += sizeof(kdh); + dumplo += size; /* Dump ELF header */ error = dumpsys_buf_write(di, (char*)&ehdr, sizeof(ehdr)); @@ -375,7 +373,7 @@ goto fail; /* Dump trailer */ - error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); + error = dump_write_pad(di, &kdh, 0, dumplo, sizeof(kdh), &size); if (error) goto fail; Index: head/sys/kern/kern_shutdown.c =================================================================== --- head/sys/kern/kern_shutdown.c +++ head/sys/kern/kern_shutdown.c @@ -88,6 +88,8 @@ #include +static MALLOC_DEFINE(M_DUMPER, "dumper", "dumper block buffer"); + #ifndef PANIC_REBOOT_WAIT_TIME #define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */ #endif @@ -848,7 +850,9 @@ return (error); if (di == NULL) { - bzero(&dumper, sizeof dumper); + if (dumper.blockbuf != NULL) + free(dumper.blockbuf, M_DUMPER); + bzero(&dumper, sizeof(dumper)); dumpdevname[0] = '\0'; return (0); } @@ -860,6 +864,7 @@ printf("set_dumper: device name truncated from '%s' -> '%s'\n", devname, dumpdevname); } + dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO); return (0); } @@ -880,6 +885,31 @@ return (di->dumper(di->priv, virtual, physical, offset, length)); } +/* Call dumper with bounds checking. */ +int +dump_write_pad(struct dumperinfo *di, void *virtual, vm_offset_t physical, + off_t offset, size_t length, size_t *size) +{ + char *temp; + int ret; + + if (length > di->blocksize) + return (ENOMEM); + + *size = di->blocksize; + if (length == di->blocksize) + temp = virtual; + else { + temp = di->blockbuf; + memset(temp + length, 0, di->blocksize - length); + memcpy(temp, virtual, length); + } + ret = dump_write(di, temp, physical, offset, *size); + + return (ret); +} + + void mkdumpheader(struct kerneldumpheader *kdh, char *magic, uint32_t archver, uint64_t dumplen, uint32_t blksz) Index: head/sys/sys/conf.h =================================================================== --- head/sys/sys/conf.h +++ head/sys/sys/conf.h @@ -328,15 +328,18 @@ struct dumperinfo { dumper_t *dumper; /* Dumping function. */ - void *priv; /* Private parts. */ - u_int blocksize; /* Size of block in bytes. */ + void *priv; /* Private parts. */ + u_int blocksize; /* Size of block in bytes. */ u_int maxiosize; /* Max size allowed for an individual I/O */ - off_t mediaoffset; /* Initial offset in bytes. */ - off_t mediasize; /* Space available in bytes. */ + off_t mediaoffset; /* Initial offset in bytes. */ + off_t mediasize; /* Space available in bytes. */ + void *blockbuf; /* Buffer for padding shorter dump blocks */ }; int set_dumper(struct dumperinfo *, const char *_devname, struct thread *td); int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); +int dump_write_pad(struct dumperinfo *, void *, vm_offset_t, off_t, size_t, + size_t *); int doadump(boolean_t); extern int dumping; /* system is dumping */