[bug report] drm/amd/display: Reuse parsing code of debugfs write buffer
Dan Carpenter
dan.carpenter at oracle.com
Mon Mar 29 13:36:09 UTC 2021
Hello Mikita Lipski,
The patch 04111850cf56: "drm/amd/display: Reuse parsing code of
debugfs write buffer" from Mar 26, 2020, leads to the following
static checker warning:
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_debugfs.c:80 parse_write_buffer_into_params()
error: 'copy_from_user()' 'wr_buf_ptr' too small (100 vs 1000000000)
drivers/gpu/drm/amd/amdgpu/../display/amdgpu_dm/amdgpu_dm_debugfs.c
64 static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size,
65 long *param, const char __user *buf,
66 int max_param_num,
67 uint8_t *param_nums)
68 {
69 char *wr_buf_ptr = NULL;
70 uint32_t wr_buf_count = 0;
71 int r;
72 char *sub_str = NULL;
73 const char delimiter[3] = {' ', '\n', '\0'};
74 uint8_t param_index = 0;
75
76 *param_nums = 0;
77
78 wr_buf_ptr = wr_buf;
79
80 r = copy_from_user(wr_buf_ptr, buf, wr_buf_size);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The "wr_buf_ptr" is a fixed size buffer and the "wr_buf_size" comes from
the user so this would be a privalege escalation bug if it weren't
debugfs.
It's better to move the allocation into this function. Remember to
free before returning.
wr_buf = memdup_user(buf, wr_buf_size + 1);
if (IS_ERR(wr_buf))
return PTR_ERR(wr_buf);
(Written in email client. Not compile tested.)
81
82 /* r is bytes not be copied */
83 if (r >= wr_buf_size) {
This test is wrong. It should have been zero instead of wr_buf_size
but that doesn't matter now that we've switched to memdup_user().
84 DRM_DEBUG_DRIVER("user data not be read\n");
85 return -EINVAL;
Don't print an error if copy_from_user() fails. Return -EFAULT instead
of -EINVAL.
86 }
87
88 /* check number of parameters. isspace could not differ space and \n */
89 while ((*wr_buf_ptr != 0xa) && (wr_buf_count < wr_buf_size)) {
90 /* skip space*/
91 while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) {
92 wr_buf_ptr++;
93 wr_buf_count++;
94 }
95
96 if (wr_buf_count == wr_buf_size)
97 break;
98
99 /* skip non-space*/
100 while ((!isspace(*wr_buf_ptr)) && (wr_buf_count < wr_buf_size)) {
101 wr_buf_ptr++;
102 wr_buf_count++;
103 }
104
105 (*param_nums)++;
106
107 if (wr_buf_count == wr_buf_size)
108 break;
109 }
110
111 if (*param_nums > max_param_num)
112 *param_nums = max_param_num;
113
114 wr_buf_ptr = wr_buf; /* reset buf pointer */
115 wr_buf_count = 0; /* number of char already checked */
116
117 while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) {
118 wr_buf_ptr++;
119 wr_buf_count++;
120 }
121
122 while (param_index < *param_nums) {
123 /* after strsep, wr_buf_ptr will be moved to after space */
124 sub_str = strsep(&wr_buf_ptr, delimiter);
This code is not checking wr_buf_count. I guess the thinking was that
the first look would find the number of words and the next loop would
use that pre-validated count. There is not a one to one relationship
between your delimeters and ispace() (tabs) but I guess your delimeter
is a subset of isspace() so that works...
This code also assumes that "wr_buf_ptr" is NUL terminated but that's
not necessarily the case so it could lead to a buffer overflow. For
example, imagine the whole buffer is full of tabs. That's counted as
one word. We're already off the end of the buffer before we call
strsep(). Or if the whole buffer is spaces except for the last
character. That's again one word and the strsep will read beyond the
end and corrupt memory.
125
126 r = kstrtol(sub_str, 16, &(param[param_index]));
127
128 if (r)
129 DRM_DEBUG_DRIVER("string to int convert error code: %d\n", r);
130
131 param_index++;
132 }
133
134 return 0;
135 }
This whole function could probably be written like (not compiled):
wr_buf = memdup_user(buf, wr_buf_size + 1);
if (IS_ERR(wr_buf))
return PTR_ERR(wr_buf);
wr_buf[wr_buf_size] = '\0';
p = wr_buf;
while (p && *p) {
if (*param_nums >= max_param_num)
goto done;
while (*p && isspace(*p))
p++;
sub_str = strsep(&p, delimiter);
ret = kstrtol(sub_str, 16, &(param[param_index]));
if (ret)
goto done;
(*param_nums)++;
}
done:
kfree(wr_buf);
return 0;
regards,
dan carpenter
More information about the amd-gfx
mailing list