kern/60436: kqueue(2): random kernel null pointer deref in EVFILT_PROC NOTE_TRACK

"[email protected] via gnats" <[email protected]>
Newsgroups gmane.os.netbsd.bugs
Message-ID <[email protected]>
>Number:         60436
>Category:       kern
>Synopsis:       kqueue(2): random kernel null pointer deref in EVFILT_PROC NOTE_TRACK
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jul 10 14:20:00 +0000 2026
>Originator:     Taylor R Campbell
>Release:        current, 11, 10
>Organization:
Go Fork Yourself, Inc.
>Environment:
>Description:

uvm_fault(0xffff9d171e3dbc80, 0x0, 2) -> e
fatal page fault in supervisor mode
trap type 6 code 0x2 rip 0xffffffff80e30191 cs 0x8 rflags 0x10246 cr2 0xb0 ilevel 0 rsp 0xffffc512650a0e08
curlwp 0xffff9d171d137400 pid 9969.9969 lowest kstack 0xffffc5126509c2c0
panic: trap
cpu6: Begin traceback...
vpanic() at netbsd:vpanic+0x189
panic() at netbsd:panic+0x3c
trap() at netbsd:trap+0xb35
--- trap (number 6) ---
_mutex_init() at netbsd:_mutex_init+0x33
knote_proc_fork() at netbsd:knote_proc_fork+0xa4
fork1() at netbsd:fork1+0x696
sys_fork() at netbsd:sys_fork+0x29
syscall() at netbsd:syscall+0x9d
--- syscall (number 2) ---
netbsd:syscall+0x9d:
cpu6: End traceback...

    144 static inline struct knote *
    145 knote_alloc(bool sleepok)
    146 {
    147 	struct knote_impl *ki;
    148 
    149 	ki = kmem_zalloc(sizeof(*ki), sleepok ? KM_SLEEP : KM_NOSLEEP);
 => 150 	mutex_init(&ki->ki_foplock, MUTEX_DEFAULT, IPL_NONE);
    151 
    152 	return KIMPL_TO_KNOTE(ki);
    153 }

https://nxr.netbsd.org/xref/src/sys/kern/kern_event.c?r=1.152#144
>How-To-Repeat:
cd /usr/tests/kernel/queue && atf-run t_proc2
>Fix:
1. Stop-gap fix:

	ki = kmem_zalloc(sizeof(*ki), sleepok ? KM_SLEEP : KM_NOSLEEP);
+	if (ki == NULL)
+		return NULL;
	mutex_init(&ki->ki_foplock, MUTEX_DEFAULT, IPL_NONE);

2. Why do we use KM_NOSLEEP at all here?  Why should this randomly fail
   when the system is swapping?  Why doesn't the one caller,
   knote_proc_fork_track, just have its caller allocate storage up
   front before any spin locks are held or knotes are in flux?

   1039 static int __noinline
   1040 knote_proc_fork_track(struct proc *p1, struct proc *p2, struct knote *okn)
   1041 {
   1042 	struct kqueue *kq = okn->kn_kq;
   1043 
   1044 	KASSERT(mutex_owned(&kq->kq_lock));
   1045 	KASSERT(mutex_owned(p1->p_lock));
...
   1053 	if (!kn_enter_flux(okn)) {
   1054 		return 0;
   1055 	}
...
   1102 	knchild = knote_alloc(false);
   1103 	kntrack = knote_alloc(false);
   1104 	if (__predict_false(knchild == NULL || kntrack == NULL)) {
   1105 		error = SET_ERROR(ENOMEM);
   1106 		goto out;
   1107 	}
...
   1199 	if (kn_leave_flux(okn)) {
   1200 		KQ_FLUX_WAKEUP(kq);
   1201 	}
   1202 
   1203 	return error;
   1204 }
...
   1204 }
   1205 
   1206 void
   1207 knote_proc_fork(struct proc *p1, struct proc *p2)
   1208 {
   1209 	struct knote *kn;
   1210 	struct kqueue *kq;
   1211 	uint32_t fflags;
   1212 
   1213 	mutex_enter(p1->p_lock);
...
   1234 		mutex_spin_enter(&kq->kq_lock);
   1235 		kn->kn_fflags |= (kn->kn_sfflags & NOTE_FORK);
   1236 		if (__predict_false(kn->kn_sfflags & NOTE_TRACK)) {
   1237 			/*
   1238 			 * This will drop kq_lock and p_lock and
   1239 			 * re-acquire them before it returns.
   1240 			 */
   1241 			if (knote_proc_fork_track(p1, p2, kn)) {
   1242 				kn->kn_fflags |= NOTE_TRACKERR;
   1243 			}

https://nxr.netbsd.org/xref/src/sys/kern/kern_event.c?r=1.152#1039
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.