Hi List.

I've stumbled across a use-after-free condition in the NTFS filesystem
driver of OpenBSD Current (HEAD, plus it's present in STABLE, and
appears to go back a few releases - I repro'ed with 5.1). I have seen
it triggered accidentally through normal use to cause a panic.

The bug revolves around the ntm_ntnodeq, a 16-entry LRU which is a
member of the ntfsmount structure. Elements are added to this by
ntfs_loadntnode, but are destroyed when removed from the LRU, without
any reference checking - so another process may have its ntnodes freed
unexectedly, causing the kernel to use memory which has been released.

The following happens:

1) Process 'foo' starts loading some data from the disk. It loads an
ntnode A, adds it to the ntnodeq (see ntfs_subr.c:353, ntfs_loadntnode
- 'TAILQ_INSERT_HEAD(&ntmp->ntm_ntnodeq, ip, i_loaded);'), and then
tsleep's waiting for a subsequent disk read to complete.
2) Process 'bar' reads sixteen (or more) ntnodes from disk. The
ntnodeq gets cycled, and ntnode A, loaded by foo previously, is freed
- see ntfs_subr.c:278, ntfs_loadntnode - 'ntfs_freentvattr(vap);')
3) Process 'foo' then attempts to use its ntnode A, which points to
freed memory.

The bad access isn't very obvious (although it does happen, as
verified by hand via a debugger) and usually goes unnoticed without
any crash or panic. If you zero out the memory before freeing it, it
is much easier to reproduce. Edit ntfs_freentvattr, found in
ntfs_subr.c:

+ memset(vap, 0, sizeof(struct ntvattr));   // (add this line)
  free(vap, M_NTFSNTVATTR, 0);              // (this line already present)

An easy way to repro the issue is to create ~100 small files, plus one
heavily fragmented file. Reading the fragmented file will cause
tsleep's between ntnode reads, while the small files can be read
repeatedly in the background. Since there are only 100 of them, they
should all fit in the cache and can be served quickly, causing them to
exhaust the LRU. Here's a script that'll create such a disk layout.
Run it in the ntfs mountpoint:

------

#!/bin/sh

# Fill disk will small files
i=0
while ( true ); do
 dd if=/dev/zero count=2 of=small_$i
 if [ $? -ne 0 ]; do
  break
 fi
 i=$(( $i + 1 ))
done

# Delete some non-sequential ones
for toDel in 0 $i 100; do
 rm small_$i
done

# Create the big file
dd if=/dev/zero of=/bigfile
------

You should be able to repro by:

0) Applying the memset above (or by being lucky)
1) Optionally reboot, which will make sure bigfile isn't cached and
will up your chances of a successful repo
2) Mounting this FS
3) Repeatedly reading 100 of the small files, causing the LRU to churn:
  while ( true ); do cat /mnt/ntfs/small_?? > /dev/null; done &
4) Reading the large file. By the time the OS has finished reading the
large file, the access of the small files will cause the first
clusters of the large file to be freed.
  cat /mnt/ntfs/big > /dev/null

The result will probably be a panic, since the memory area being used
by the kernel is free'd. Manual analysis verifies that the memory
being accessed is, indeed, freed. A sample panic (full logs below):

ntfs_readntvattr_plain: POSSIBLE RUN ERROR
ntfs_readattr_plain: ntfs_readntvattr_plain failed: o: 1179648, s: 65536
ntfs_readattr_plain: attrib: 0 - 0
panic: ntfs_ntrele: ino: 159 usecount: -1
Stopped at      Debugger+0x9:   leave
RUN AT LEAST 'trace' AND 'ps' AND INCLUDE OUTPUT WHEN REPORTING THIS PANIC!
DO NOT EVEN BOTHER REPORTING THIS WITHOUT INCLUDING THAT INFORMATION!
ddb> trace
Debugger() at Debugger+0x9
panic() at panic+0xfe
ntfs_ntrele() at ntfs_ntrele+0x28
ntfs_ntvattrrele() at ntfs_ntvattrrele+0x11
ntfs_readattr() at ntfs_readattr+0x135
ntfs_read() at ntfs_read+0x69
VOP_READ() at VOP_READ+0x3f
vn_read() at vn_read+0xa1
dofilereadv() at dofilereadv+0x1c4
sys_read() at sys_read+0x89
syscall() at syscall+0x192
--- syscall (number 3) ---
end trace frame: 0x0, count: -11

My repros were done on a VMWare Workstation VM, configured to have 1
CPU core, 256MB RAM, and to have a 64bit guest. I have verified this
issue on physical hardware also.

I'm guessing that the call to ntfs_freentvattr is in error, and it
should be a call to ntfs_ntput, but I'm unfamilliar with OpenBSD and
don't know if this is correct (especially in regard to locking
semantics).

Hope this is helpful. Don't hesitate to contact me if there's any more
info you need, or just to keep me updated. If you end up putting out
an advisory, I'd be very grateful if you could credit me on it - it
might help me find a job!

- Aliz Hammond

Full ddb log:

rebooting...
>> OpenBSD/amd64 BOOT 3.28
boot>
booting hd0a:/bsd:
entry point at 0x1000160 [7205c766, 34000004, 24448b12, 9060a304]
[ using 932984 bytes of bsd ELF symbol table ]
Copyright (c) 1982, 1986, 1989, 1991, 1993
The Regents of the University of California.  All rights reserved.
Copyright (c) 1995-2015 OpenBSD. All rights reserved.  http://www.OpenBSD.org
OpenBSD 5.8-beta (GENERIC) #0: Mon Jun 22 04:41:57 BST 2015
    root@foo.localdomain:/usr/src/src/sys/arch/amd64/compile/GENERIC
real mem = 251592704 (239MB)
avail mem = 240250880 (229MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.4 @ 0xe0010 (98 entries)
bios0: vendor Phoenix Technologies LTD version "6.00" date 12/31/2009
bios0: VMware, Inc. VMware Virtual Platform
acpi0 at bios0: rev 2
acpi0: sleep states S0 S1 S4 S5
acpi0: tables DSDT FACP BOOT APIC MCFG SRAT
acpi0: wakeup devices PCI0(S3) USB_(S1) P2P0(S3) S1F0(S3) S2F0(S3)
S3F0(S3) S4F0(S3) S5F0(S3) S6F0(S3) S7F0(S3) S8F0(S3) S9F0(S3)
Z00P(S3) Z00Q(S3) Z00R(S3) Z00S(S3) [...]
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Xeon(R) CPU L5420 @ 2.50GHz, 2500.10 MHz
cpu0: 
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,SSE3,SSSE3,CX16,SSE4.1,HV,NXE,LONG,LAHF,PERF,SENSOR
cpu0: 6MB 64b/line 16-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 8 var ranges, 88 fixed ranges
cpu0: apic clock running at 65MHz
ioapic0 at mainbus0: apid 1 pa 0xfec00000, version 11, 24 pins
acpimcfg0 at acpi0 addr 0xe0000000, bus 0-255
acpiprt0 at acpi0: bus 0 (PCI0)
acpicpu0 at acpi0
acpibat0 at acpi0: BAT1 not present
acpibat1 at acpi0: BAT2 not present
acpiac0 at acpi0: AC unit online
acpibtn0 at acpi0: SLPB
acpibtn1 at acpi0: LID_
vmt0 at mainbus0
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel 82443BX AGP" rev 0x01
ppb0 at pci0 dev 1 function 0 "Intel 82443BX AGP" rev 0x01
pci1 at ppb0 bus 1
pcib0 at pci0 dev 7 function 0 "Intel 82371AB PIIX4 ISA" rev 0x08
pciide0 at pci0 dev 7 function 1 "Intel 82371AB IDE" rev 0x01: DMA,
channel 0 configured to compatibility, channel 1 configured to
compatibility
pciide0: channel 0 disabled (no drives)
atapiscsi0 at pciide0 channel 1 drive 0
scsibus1 at atapiscsi0: 2 targets
cd0 at scsibus1 targ 0 lun 0: <NECVMWar, VMware IDE CDR10, 1.00> ATAPI
5/cdrom removable
cd0(pciide0:1:0): using PIO mode 4, Ultra-DMA mode 2
piixpm0 at pci0 dev 7 function 3 "Intel 82371AB Power" rev 0x08: SMBus disabled
"VMware VMCI" rev 0x10 at pci0 dev 7 function 7 not configured
vga1 at pci0 dev 15 function 0 "VMware SVGA II" rev 0x00
wsdisplay0 at vga1 mux 1: console (80x25, vt100 emulation)
wsdisplay0: screen 1-5 added (80x25, vt100 emulation)
mpi0 at pci0 dev 16 function 0 "Symbios Logic 53c1030" rev 0x01: apic 1 int 17
mpi0: 0, firmware 1.3.41.32
scsibus2 at mpi0: 16 targets, initiator 7
sd0 at scsibus2 targ 0 lun 0: <VMware,, VMware Virtual S, 1.0> SCSI2
0/direct fixed
sd0: 20480MB, 512 bytes/sector, 41943040 sectors
mpi0: target 0 Sync at 160MHz width 16bit offset 127 QAS 1 DT 1 IU 1
ppb1 at pci0 dev 17 function 0 "VMware PCI" rev 0x02
pci2 at ppb1 bus 2
em0 at pci2 dev 0 function 0 "Intel 82545EM" rev 0x01: apic 1 int 18,
address 00:0c:29:ef:91:73
ppb2 at pci0 dev 21 function 0 "VMware PCIE" rev 0x01
pci3 at ppb2 bus 3
ppb3 at pci0 dev 21 function 1 "VMware PCIE" rev 0x01
pci4 at ppb3 bus 4
ppb4 at pci0 dev 21 function 2 "VMware PCIE" rev 0x01
pci5 at ppb4 bus 5
ppb5 at pci0 dev 21 function 3 "VMware PCIE" rev 0x01
pci6 at ppb5 bus 6
ppb6 at pci0 dev 21 function 4 "VMware PCIE" rev 0x01
pci7 at ppb6 bus 7
ppb7 at pci0 dev 21 function 5 "VMware PCIE" rev 0x01
pci8 at ppb7 bus 8
ppb8 at pci0 dev 21 function 6 "VMware PCIE" rev 0x01
pci9 at ppb8 bus 9
ppb9 at pci0 dev 21 function 7 "VMware PCIE" rev 0x01
pci10 at ppb9 bus 10
ppb10 at pci0 dev 22 function 0 "VMware PCIE" rev 0x01
pci11 at ppb10 bus 11
ppb11 at pci0 dev 22 function 1 "VMware PCIE" rev 0x01
pci12 at ppb11 bus 12
ppb12 at pci0 dev 22 function 2 "VMware PCIE" rev 0x01
pci13 at ppb12 bus 13
ppb13 at pci0 dev 22 function 3 "VMware PCIE" rev 0x01
pci14 at ppb13 bus 14
ppb14 at pci0 dev 22 function 4 "VMware PCIE" rev 0x01
pci15 at ppb14 bus 15
ppb15 at pci0 dev 22 function 5 "VMware PCIE" rev 0x01
pci16 at ppb15 bus 16
ppb16 at pci0 dev 22 function 6 "VMware PCIE" rev 0x01
pci17 at ppb16 bus 17
ppb17 at pci0 dev 22 function 7 "VMware PCIE" rev 0x01
pci18 at ppb17 bus 18
ppb18 at pci0 dev 23 function 0 "VMware PCIE" rev 0x01
pci19 at ppb18 bus 19
ppb19 at pci0 dev 23 function 1 "VMware PCIE" rev 0x01
pci20 at ppb19 bus 20
ppb20 at pci0 dev 23 function 2 "VMware PCIE" rev 0x01
pci21 at ppb20 bus 21
ppb21 at pci0 dev 23 function 3 "VMware PCIE" rev 0x01
pci22 at ppb21 bus 22
ppb22 at pci0 dev 23 function 4 "VMware PCIE" rev 0x01
pci23 at ppb22 bus 23
ppb23 at pci0 dev 23 function 5 "VMware PCIE" rev 0x01
pci24 at ppb23 bus 24
ppb24 at pci0 dev 23 function 6 "VMware PCIE" rev 0x01
pci25 at ppb24 bus 25
ppb25 at pci0 dev 23 function 7 "VMware PCIE" rev 0x01
pci26 at ppb25 bus 26
ppb26 at pci0 dev 24 function 0 "VMware PCIE" rev 0x01
pci27 at ppb26 bus 27
ppb27 at pci0 dev 24 function 1 "VMware PCIE" rev 0x01
pci28 at ppb27 bus 28
ppb28 at pci0 dev 24 function 2 "VMware PCIE" rev 0x01
pci29 at ppb28 bus 29
ppb29 at pci0 dev 24 function 3 "VMware PCIE" rev 0x01
pci30 at ppb29 bus 30
ppb30 at pci0 dev 24 function 4 "VMware PCIE" rev 0x01
pci31 at ppb30 bus 31
ppb31 at pci0 dev 24 function 5 "VMware PCIE" rev 0x01
pci32 at ppb31 bus 32
ppb32 at pci0 dev 24 function 6 "VMware PCIE" rev 0x01
pci33 at ppb32 bus 33
ppb33 at pci0 dev 24 function 7 "VMware PCIE" rev 0x01
pci34 at ppb33 bus 34
isa0 at pcib0
isadma0 at isa0
fdc0 at isa0 port 0x3f0/6 irq 6 drq 2
com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo
com0: console
com1 at isa0 port 0x2f8/8 irq 3: ns16550a, 16 byte fifo
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
lpt0 at isa0 port 0x378/4 irq 7
vscsi0 at root
scsibus3 at vscsi0: 256 targets
softraid0 at root
scsibus4 at softraid0: 256 targets
root on sd0a (a5b771226ef737bb.a) swap on sd0b dump on sd0b
WARNING: / was not properly unmounted
Automatic boot in progress: starting file system checks.
/dev/sd0a (a5b771226ef737bb.a): 1763 files, 84111 used, 289936 free
(32 frags, 36238 blocks, 0.0% fragmentation)
/dev/sd0a (a5b771226ef737bb.a): MARKING FILE SYSTEM CLEAN
/dev/sd0k (a5b771226ef737bb.k): 10 files, 9 used, 3666686 free (14
frags, 458334 blocks, 0.0% fragmentation)
/dev/sd0k (a5b771226ef737bb.k): MARKING FILE SYSTEM CLEAN
/dev/sd0d (a5b771226ef737bb.d): 6 files, 5 used, 596570 free (42
frags, 74566 blocks, 0.0% fragmentation)
/dev/sd0d (a5b771226ef737bb.d): MARKING FILE SYSTEM CLEAN
/dev/sd0f (a5b771226ef737bb.f): 11744 files, 189302 used, 598985 free
(209 frags, 74847 blocks, 0.0% fragmentation)
/dev/sd0f (a5b771226ef737bb.f): MARKING FILE SYSTEM CLEAN
/dev/sd0g (a5b771226ef737bb.g): 9183 files, 97521 used, 360782 free
(1862 frags, 44865 blocks, 0.4% fragmentation)
/dev/sd0g (a5b771226ef737bb.g): MARKING FILE SYSTEM CLEAN
/dev/sd0h (a5b771226ef737bb.h): 111 files, 115 used, 1701804 free (68
frags, 212717 blocks, 0.0% fragmentation)
/dev/sd0h (a5b771226ef737bb.h): MARKING FILE SYSTEM CLEAN
/dev/sd0j (a5b771226ef737bb.j): 1 files, 1 used, 921678 free (14
frags, 115208 blocks, 0.0% fragmentation)
/dev/sd0j (a5b771226ef737bb.j): MARKING FILE SYSTEM CLEAN
/dev/sd0i (a5b771226ef737bb.i): 14268 files, 84608 used, 564431 free
(183 frags, 70531 blocks, 0.0% fragmentation)
/dev/sd0i (a5b771226ef737bb.i): MARKING FILE SYSTEM CLEAN
/dev/sd0e (a5b771226ef737bb.e): 303 files, 3138 used, 908661 free (53
frags, 113576 blocks, 0.0% fragmentation)
/dev/sd0e (a5b771226ef737bb.e): MARKING FILE SYSTEM CLEAN
setting tty flags
pf enabled
machdep.allowaperture: 0 -> 2
starting network
DHCPREQUEST on em0 to 255.255.255.255
DHCPACK from 192.168.174.254 (00:50:56:ea:e4:a7)
bound to 192.168.174.139 -- renewal in 900 seconds.
starting early daemons: syslogd pflogd(failed).
starting RPC daemons:.
savecore: no core dump
checking quotas: done.
clearing /tmp
kern.securelevel: 0 -> 1
creating runtime link editor directory cache.
preserving editor files.
starting network daemons: smtpd sndiod.
starting local daemons: cron.
Mon Jun 22 13:51:45 BST 2015
ntfs_readntvattr_plain: POSSIBLE RUN ERROR
ntfs_readattr_plain: ntfs_readntvattr_plain failed: o: 1179648, s: 65536
ntfs_readattr_plain: attrib: 0 - 0
panic: ntfs_ntrele: ino: 159 usecount: -1
Stopped at      Debugger+0x9:   leave
RUN AT LEAST 'trace' AND 'ps' AND INCLUDE OUTPUT WHEN REPORTING THIS PANIC!
DO NOT EVEN BOTHER REPORTING THIS WITHOUT INCLUDING THAT INFORMATION!
ddb> trace
Debugger() at Debugger+0x9
panic() at panic+0xfe
ntfs_ntrele() at ntfs_ntrele+0x28
ntfs_ntvattrrele() at ntfs_ntvattrrele+0x11
ntfs_readattr() at ntfs_readattr+0x135
ntfs_read() at ntfs_read+0x69
VOP_READ() at VOP_READ+0x3f
vn_read() at vn_read+0xa1
dofilereadv() at dofilereadv+0x1c4
sys_read() at sys_read+0x89
syscall() at syscall+0x192
--- syscall (number 3) ---
end trace frame: 0x0, count: -11
0x9696706d3a:
ddb> ps
   PID   PPID   PGRP    UID  S       FLAGS  WAIT          COMMAND
 23411   1151   1151      0  2         0x3                cat
*16948  20938  16948      0  7         0x3                cat
  1151  20938   1151      0  3        0x8b  pause         sh
 15738      1  15738      0  3        0x83  ttyin         getty
 17533      1  17533      0  3        0x83  ttyin         getty
  1665      1   1665      0  3        0x83  ttyin         getty
 31938      1  31938      0  3        0x83  ttyin         getty
 20938      1  20938      0  3        0x8b  pause         ksh
 30503      1  30503      0  3        0x80  poll          cron
 21467      1  21467     99  3        0x90  poll          sndiod
 11796  16973  16973     95  3        0x90  kqread        smtpd
     6  16973  16973     95  3        0x90  kqread        smtpd
 23807  16973  16973     95  3        0x90  kqread        smtpd
 12206  16973  16973     95  3        0x90  kqread        smtpd
 28488  16973  16973     95  3        0x90  kqread        smtpd
 30803  16973  16973    103  3        0x90  kqread        smtpd
 16973      1  16973      0  3        0x80  kqread        smtpd
 20314   3277   3277     73  2        0x90                syslogd
  3277      1   3277      0  3        0x80  netio         syslogd
 31874      1  31874     77  3        0x90  poll          dhclient
  5710      1   5710      0  3        0x80  poll          dhclient
 12169      0      0      0  2     0x14200                zerothread
 17046      0      0      0  3     0x14200  aiodoned      aiodoned
  7135      0      0      0  3     0x14200  syncer        update
  4702      0      0      0  3     0x14200  cleaner       cleaner
 26531      0      0      0  3     0x14200  reaper        reaper
  5615      0      0      0  3     0x14200  pgdaemon      pagedaemon
   786      0      0      0  3     0x14200  bored         crypto
 21707      0      0      0  3     0x14200  pftm          pfpurge
 13257      0      0      0  3  0x40014200  acpi0         acpi0
 21965      0      0      0  3     0x14200  bored         sensors
  5400      0      0      0  3     0x14200  bored         softnet
  7731      0      0      0  3     0x14200  bored         systqmp
 27942      0      0      0  3     0x14200  bored         systq
   495      0      0      0  3  0x40014200                idle0
     1      0      1      0  3        0x82  wait          init
     0     -1      0      0  3     0x10200  scheduler     swapper

Reply via email to