Skip to content

Commit

Permalink
profiling: fix shift-out-of-bounds bugs
Browse files Browse the repository at this point in the history
Syzbot reported shift-out-of-bounds bug in profile_init().
The problem was in incorrect prof_shift. Since prof_shift value comes from
userspace we need to clamp this value into [0, BITS_PER_LONG -1]
boundaries.

Second possible shiht-out-of-bounds was found by Tetsuo:
sample_step local variable in read_profile() had "unsigned int" type,
but prof_shift allows to make a BITS_PER_LONG shift. So, to prevent
possible shiht-out-of-bounds sample_step type was changed to
"unsigned long".

Also, "unsigned short int" will be sufficient for storing
[0, BITS_PER_LONG] value, that's why there is no need for
"unsigned long" prof_shift.

Link: https://lkml.kernel.org/r/[email protected]
Fixes: 1da177e ("Linux-2.6.12-rc2")
Reported-and-tested-by: [email protected]
Suggested-by: Tetsuo Handa <[email protected]>
Signed-off-by: Pavel Skripkin <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Steven Rostedt <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
pskrgag authored and torvalds committed Sep 8, 2021
1 parent 3c91dda commit 2d186af
Showing 1 changed file with 11 additions and 10 deletions.
21 changes: 11 additions & 10 deletions kernel/profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ struct profile_hit {
#define NR_PROFILE_GRP (NR_PROFILE_HIT/PROFILE_GRPSZ)

static atomic_t *prof_buffer;
static unsigned long prof_len, prof_shift;
static unsigned long prof_len;
static unsigned short int prof_shift;

int prof_on __read_mostly;
EXPORT_SYMBOL_GPL(prof_on);
Expand All @@ -67,8 +68,8 @@ int profile_setup(char *str)
if (str[strlen(sleepstr)] == ',')
str += strlen(sleepstr) + 1;
if (get_option(&str, &par))
prof_shift = par;
pr_info("kernel sleep profiling enabled (shift: %ld)\n",
prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
pr_info("kernel sleep profiling enabled (shift: %u)\n",
prof_shift);
#else
pr_warn("kernel sleep profiling requires CONFIG_SCHEDSTATS\n");
Expand All @@ -78,21 +79,21 @@ int profile_setup(char *str)
if (str[strlen(schedstr)] == ',')
str += strlen(schedstr) + 1;
if (get_option(&str, &par))
prof_shift = par;
pr_info("kernel schedule profiling enabled (shift: %ld)\n",
prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
pr_info("kernel schedule profiling enabled (shift: %u)\n",
prof_shift);
} else if (!strncmp(str, kvmstr, strlen(kvmstr))) {
prof_on = KVM_PROFILING;
if (str[strlen(kvmstr)] == ',')
str += strlen(kvmstr) + 1;
if (get_option(&str, &par))
prof_shift = par;
pr_info("kernel KVM profiling enabled (shift: %ld)\n",
prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
pr_info("kernel KVM profiling enabled (shift: %u)\n",
prof_shift);
} else if (get_option(&str, &par)) {
prof_shift = par;
prof_shift = clamp(par, 0, BITS_PER_LONG - 1);
prof_on = CPU_PROFILING;
pr_info("kernel profiling enabled (shift: %ld)\n",
pr_info("kernel profiling enabled (shift: %u)\n",
prof_shift);
}
return 1;
Expand Down Expand Up @@ -468,7 +469,7 @@ read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos)
unsigned long p = *ppos;
ssize_t read;
char *pnt;
unsigned int sample_step = 1 << prof_shift;
unsigned long sample_step = 1UL << prof_shift;

profile_flip_buffers();
if (p >= (prof_len+1)*sizeof(unsigned int))
Expand Down

0 comments on commit 2d186af

Please sign in to comment.