Skip to content

Commit f86b0aa

Browse files
Kalesh Singhrostedt
authored andcommitted
tracing/histogram: Fix UAF in destroy_hist_field()
Calling destroy_hist_field() on an expression will recursively free any operands associated with the expression. If during expression parsing the operands of the expression are already set when an error is encountered, there is no need to explicity free the operands. Doing so will result in destroy_hist_field() being called twice for the operands and lead to a use-after-free (UAF) error. If the operands are associated with the expression, only call destroy_hist_field() on the expression since the operands will be recursively freed. Link: https://lore.kernel.org/all/CAHk-=wgcrEbFgkw9720H3tW-AhHOoEKhYwZinYJw4FpzSaJ6_Q@mail.gmail.com/ Link: https://lkml.kernel.org/r/[email protected] Suggested-by: Linus Torvalds <[email protected]> Signed-off-by: Kalesh Singh <[email protected]> Fixes: 8b5d46f ("tracing/histogram: Optimize division by constants") Reported-by: kernel test robot <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 8ab7745 commit f86b0aa

File tree

1 file changed

+22
-19
lines changed

1 file changed

+22
-19
lines changed

kernel/trace/trace_events_hist.c

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,42 +2576,40 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
25762576

25772577
/* Split the expression string at the root operator */
25782578
if (!sep)
2579-
goto free;
2579+
return ERR_PTR(-EINVAL);
2580+
25802581
*sep = '\0';
25812582
operand1_str = str;
25822583
str = sep+1;
25832584

25842585
/* Binary operator requires both operands */
25852586
if (*operand1_str == '\0' || *str == '\0')
2586-
goto free;
2587+
return ERR_PTR(-EINVAL);
25872588

25882589
operand_flags = 0;
25892590

25902591
/* LHS of string is an expression e.g. a+b in a+b+c */
25912592
operand1 = parse_expr(hist_data, file, operand1_str, operand_flags, NULL, n_subexprs);
2592-
if (IS_ERR(operand1)) {
2593-
ret = PTR_ERR(operand1);
2594-
operand1 = NULL;
2595-
goto free;
2596-
}
2593+
if (IS_ERR(operand1))
2594+
return ERR_CAST(operand1);
2595+
25972596
if (operand1->flags & HIST_FIELD_FL_STRING) {
25982597
hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(operand1_str));
25992598
ret = -EINVAL;
2600-
goto free;
2599+
goto free_op1;
26012600
}
26022601

26032602
/* RHS of string is another expression e.g. c in a+b+c */
26042603
operand_flags = 0;
26052604
operand2 = parse_expr(hist_data, file, str, operand_flags, NULL, n_subexprs);
26062605
if (IS_ERR(operand2)) {
26072606
ret = PTR_ERR(operand2);
2608-
operand2 = NULL;
2609-
goto free;
2607+
goto free_op1;
26102608
}
26112609
if (operand2->flags & HIST_FIELD_FL_STRING) {
26122610
hist_err(file->tr, HIST_ERR_INVALID_STR_OPERAND, errpos(str));
26132611
ret = -EINVAL;
2614-
goto free;
2612+
goto free_operands;
26152613
}
26162614

26172615
switch (field_op) {
@@ -2629,12 +2627,12 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
26292627
break;
26302628
default:
26312629
ret = -EINVAL;
2632-
goto free;
2630+
goto free_operands;
26332631
}
26342632

26352633
ret = check_expr_operands(file->tr, operand1, operand2, &var1, &var2);
26362634
if (ret)
2637-
goto free;
2635+
goto free_operands;
26382636

26392637
operand_flags = var1 ? var1->flags : operand1->flags;
26402638
operand2_flags = var2 ? var2->flags : operand2->flags;
@@ -2653,12 +2651,13 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
26532651
expr = create_hist_field(hist_data, NULL, flags, var_name);
26542652
if (!expr) {
26552653
ret = -ENOMEM;
2656-
goto free;
2654+
goto free_operands;
26572655
}
26582656

26592657
operand1->read_once = true;
26602658
operand2->read_once = true;
26612659

2660+
/* The operands are now owned and free'd by 'expr' */
26622661
expr->operands[0] = operand1;
26632662
expr->operands[1] = operand2;
26642663

@@ -2669,7 +2668,7 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
26692668
if (!divisor) {
26702669
hist_err(file->tr, HIST_ERR_DIVISION_BY_ZERO, errpos(str));
26712670
ret = -EDOM;
2672-
goto free;
2671+
goto free_expr;
26732672
}
26742673

26752674
/*
@@ -2709,18 +2708,22 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
27092708
expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
27102709
if (!expr->type) {
27112710
ret = -ENOMEM;
2712-
goto free;
2711+
goto free_expr;
27132712
}
27142713

27152714
expr->name = expr_str(expr, 0);
27162715
}
27172716

27182717
return expr;
2719-
free:
2720-
destroy_hist_field(operand1, 0);
2718+
2719+
free_operands:
27212720
destroy_hist_field(operand2, 0);
2722-
destroy_hist_field(expr, 0);
2721+
free_op1:
2722+
destroy_hist_field(operand1, 0);
2723+
return ERR_PTR(ret);
27232724

2725+
free_expr:
2726+
destroy_hist_field(expr, 0);
27242727
return ERR_PTR(ret);
27252728
}
27262729

0 commit comments

Comments
 (0)