| From fade8b3cf37785297b4f8a9bbd13ab107208af5a Mon Sep 17 00:00:00 2001 |
| From: Hans de Goede <hdegoede@redhat.com> |
| Date: Mon, 18 Nov 2019 16:51:22 +0100 |
| Subject: [PATCH] drm/modes: parse_cmdline: Fix possible reference past |
| end of string |
| |
| Commit 8582e244e5fe72d2e9ace186fa8f3ed3bb4122e1 upstream. |
| |
| Before this commit, if the last option of a video=... option is for |
| example "rotate" without a "=<value>" after it then delim will point to |
| the terminating 0 of the string, and value which is sets to <delim + 1> |
| will point one position past the end of the string. |
| |
| This commit fixes this by enforcing that the contents of delim equals '=' |
| as it should be for options which take a value, this check is done in a |
| new drm_mode_parse_cmdline_int helper function which factors out the |
| common integer parsing code for all the options which take an int. |
| |
| Acked-by: Maxime Ripard <mripard@kernel.org> |
| Signed-off-by: Hans de Goede <hdegoede@redhat.com> |
| Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-1-hdegoede@redhat.com |
| --- |
| drivers/gpu/drm/drm_modes.c | 68 ++++++++++++++++--------------------- |
| 1 file changed, 30 insertions(+), 38 deletions(-) |
| |
| --- a/drivers/gpu/drm/drm_modes.c |
| +++ b/drivers/gpu/drm/drm_modes.c |
| @@ -1568,11 +1568,34 @@ static int drm_mode_parse_cmdline_res_mo |
| return 0; |
| } |
| |
| +static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret) |
| +{ |
| + const char *value; |
| + char *endp; |
| + |
| + /* |
| + * delim must point to the '=', otherwise it is a syntax error and |
| + * if delim points to the terminating zero, then delim + 1 wil point |
| + * past the end of the string. |
| + */ |
| + if (*delim != '=') |
| + return -EINVAL; |
| + |
| + value = delim + 1; |
| + *int_ret = simple_strtol(value, &endp, 10); |
| + |
| + /* Make sure we have parsed something */ |
| + if (endp == value) |
| + return -EINVAL; |
| + |
| + return 0; |
| +} |
| + |
| static int drm_mode_parse_cmdline_options(char *str, size_t len, |
| const struct drm_connector *connector, |
| struct drm_cmdline_mode *mode) |
| { |
| - unsigned int rotation = 0; |
| + unsigned int deg, margin, rotation = 0; |
| char *sep = str; |
| |
| while ((sep = strchr(sep, ','))) { |
| @@ -1588,13 +1611,7 @@ static int drm_mode_parse_cmdline_option |
| } |
| |
| if (!strncmp(option, "rotate", delim - option)) { |
| - const char *value = delim + 1; |
| - unsigned int deg; |
| - |
| - deg = simple_strtol(value, &sep, 10); |
| - |
| - /* Make sure we have parsed something */ |
| - if (sep == value) |
| + if (drm_mode_parse_cmdline_int(delim, °)) |
| return -EINVAL; |
| |
| switch (deg) { |
| @@ -1619,57 +1636,32 @@ static int drm_mode_parse_cmdline_option |
| } |
| } else if (!strncmp(option, "reflect_x", delim - option)) { |
| rotation |= DRM_MODE_REFLECT_X; |
| - sep = delim; |
| } else if (!strncmp(option, "reflect_y", delim - option)) { |
| rotation |= DRM_MODE_REFLECT_Y; |
| - sep = delim; |
| } else if (!strncmp(option, "margin_right", delim - option)) { |
| - const char *value = delim + 1; |
| - unsigned int margin; |
| - |
| - margin = simple_strtol(value, &sep, 10); |
| - |
| - /* Make sure we have parsed something */ |
| - if (sep == value) |
| + if (drm_mode_parse_cmdline_int(delim, &margin)) |
| return -EINVAL; |
| |
| mode->tv_margins.right = margin; |
| } else if (!strncmp(option, "margin_left", delim - option)) { |
| - const char *value = delim + 1; |
| - unsigned int margin; |
| - |
| - margin = simple_strtol(value, &sep, 10); |
| - |
| - /* Make sure we have parsed something */ |
| - if (sep == value) |
| + if (drm_mode_parse_cmdline_int(delim, &margin)) |
| return -EINVAL; |
| |
| mode->tv_margins.left = margin; |
| } else if (!strncmp(option, "margin_top", delim - option)) { |
| - const char *value = delim + 1; |
| - unsigned int margin; |
| - |
| - margin = simple_strtol(value, &sep, 10); |
| - |
| - /* Make sure we have parsed something */ |
| - if (sep == value) |
| + if (drm_mode_parse_cmdline_int(delim, &margin)) |
| return -EINVAL; |
| |
| mode->tv_margins.top = margin; |
| } else if (!strncmp(option, "margin_bottom", delim - option)) { |
| - const char *value = delim + 1; |
| - unsigned int margin; |
| - |
| - margin = simple_strtol(value, &sep, 10); |
| - |
| - /* Make sure we have parsed something */ |
| - if (sep == value) |
| + if (drm_mode_parse_cmdline_int(delim, &margin)) |
| return -EINVAL; |
| |
| mode->tv_margins.bottom = margin; |
| } else { |
| return -EINVAL; |
| } |
| + sep = delim; |
| } |
| |
| if (!(rotation & DRM_MODE_ROTATE_MASK)) |