Skip to content

Commit 7ff94f2

Browse files
Kui-Feng LeeAlexei Starovoitov
authored andcommitted
bpf: keep a reference to the mm, in case the task is dead.
Fix the system crash that happens when a task iterator travel through vma of tasks. In task iterators, we used to access mm by following the pointer on the task_struct; however, the death of a task will clear the pointer, even though we still hold the task_struct. That can cause an unexpected crash for a null pointer when an iterator is visiting a task that dies during the visit. Keeping a reference of mm on the iterator ensures we always have a valid pointer to mm. Co-developed-by: Song Liu <[email protected]> Signed-off-by: Song Liu <[email protected]> Signed-off-by: Kui-Feng Lee <[email protected]> Reported-by: Nathan Slingerland <[email protected]> Acked-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 8f161ca commit 7ff94f2

File tree

1 file changed

+27
-12
lines changed

1 file changed

+27
-12
lines changed

kernel/bpf/task_iter.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ struct bpf_iter_seq_task_vma_info {
438438
*/
439439
struct bpf_iter_seq_task_common common;
440440
struct task_struct *task;
441+
struct mm_struct *mm;
441442
struct vm_area_struct *vma;
442443
u32 tid;
443444
unsigned long prev_vm_start;
@@ -456,16 +457,19 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
456457
enum bpf_task_vma_iter_find_op op;
457458
struct vm_area_struct *curr_vma;
458459
struct task_struct *curr_task;
460+
struct mm_struct *curr_mm;
459461
u32 saved_tid = info->tid;
460462

461463
/* If this function returns a non-NULL vma, it holds a reference to
462-
* the task_struct, and holds read lock on vma->mm->mmap_lock.
464+
* the task_struct, holds a refcount on mm->mm_users, and holds
465+
* read lock on vma->mm->mmap_lock.
463466
* If this function returns NULL, it does not hold any reference or
464467
* lock.
465468
*/
466469
if (info->task) {
467470
curr_task = info->task;
468471
curr_vma = info->vma;
472+
curr_mm = info->mm;
469473
/* In case of lock contention, drop mmap_lock to unblock
470474
* the writer.
471475
*
@@ -504,13 +508,15 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
504508
* 4.2) VMA2 and VMA2' covers different ranges, process
505509
* VMA2'.
506510
*/
507-
if (mmap_lock_is_contended(curr_task->mm)) {
511+
if (mmap_lock_is_contended(curr_mm)) {
508512
info->prev_vm_start = curr_vma->vm_start;
509513
info->prev_vm_end = curr_vma->vm_end;
510514
op = task_vma_iter_find_vma;
511-
mmap_read_unlock(curr_task->mm);
512-
if (mmap_read_lock_killable(curr_task->mm))
515+
mmap_read_unlock(curr_mm);
516+
if (mmap_read_lock_killable(curr_mm)) {
517+
mmput(curr_mm);
513518
goto finish;
519+
}
514520
} else {
515521
op = task_vma_iter_next_vma;
516522
}
@@ -535,42 +541,47 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
535541
op = task_vma_iter_find_vma;
536542
}
537543

538-
if (!curr_task->mm)
544+
curr_mm = get_task_mm(curr_task);
545+
if (!curr_mm)
539546
goto next_task;
540547

541-
if (mmap_read_lock_killable(curr_task->mm))
548+
if (mmap_read_lock_killable(curr_mm)) {
549+
mmput(curr_mm);
542550
goto finish;
551+
}
543552
}
544553

545554
switch (op) {
546555
case task_vma_iter_first_vma:
547-
curr_vma = find_vma(curr_task->mm, 0);
556+
curr_vma = find_vma(curr_mm, 0);
548557
break;
549558
case task_vma_iter_next_vma:
550-
curr_vma = find_vma(curr_task->mm, curr_vma->vm_end);
559+
curr_vma = find_vma(curr_mm, curr_vma->vm_end);
551560
break;
552561
case task_vma_iter_find_vma:
553562
/* We dropped mmap_lock so it is necessary to use find_vma
554563
* to find the next vma. This is similar to the mechanism
555564
* in show_smaps_rollup().
556565
*/
557-
curr_vma = find_vma(curr_task->mm, info->prev_vm_end - 1);
566+
curr_vma = find_vma(curr_mm, info->prev_vm_end - 1);
558567
/* case 1) and 4.2) above just use curr_vma */
559568

560569
/* check for case 2) or case 4.1) above */
561570
if (curr_vma &&
562571
curr_vma->vm_start == info->prev_vm_start &&
563572
curr_vma->vm_end == info->prev_vm_end)
564-
curr_vma = find_vma(curr_task->mm, curr_vma->vm_end);
573+
curr_vma = find_vma(curr_mm, curr_vma->vm_end);
565574
break;
566575
}
567576
if (!curr_vma) {
568577
/* case 3) above, or case 2) 4.1) with vma->next == NULL */
569-
mmap_read_unlock(curr_task->mm);
578+
mmap_read_unlock(curr_mm);
579+
mmput(curr_mm);
570580
goto next_task;
571581
}
572582
info->task = curr_task;
573583
info->vma = curr_vma;
584+
info->mm = curr_mm;
574585
return curr_vma;
575586

576587
next_task:
@@ -579,6 +590,7 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
579590

580591
put_task_struct(curr_task);
581592
info->task = NULL;
593+
info->mm = NULL;
582594
info->tid++;
583595
goto again;
584596

@@ -587,6 +599,7 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
587599
put_task_struct(curr_task);
588600
info->task = NULL;
589601
info->vma = NULL;
602+
info->mm = NULL;
590603
return NULL;
591604
}
592605

@@ -658,7 +671,9 @@ static void task_vma_seq_stop(struct seq_file *seq, void *v)
658671
*/
659672
info->prev_vm_start = ~0UL;
660673
info->prev_vm_end = info->vma->vm_end;
661-
mmap_read_unlock(info->task->mm);
674+
mmap_read_unlock(info->mm);
675+
mmput(info->mm);
676+
info->mm = NULL;
662677
put_task_struct(info->task);
663678
info->task = NULL;
664679
}

0 commit comments

Comments
 (0)