Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dm: reject trailing characters in sccanf input

Device mapper uses sscanf to convert arguments to numbers. The problem is that
the way we use it ignores additional unmatched characters in the scanned string.

For example, this `if (sscanf(string, "%d", &number) == 1)' will match a number,
but also it will match number with some garbage appended, like "123abc".

As a result, device mapper accepts garbage after some numbers. For example
the command `dmsetup create vg1-new --table "0 16384 linear 254:1bla 34816bla"'
will pass without an error.

This patch fixes all sscanf uses in device mapper. It appends "%c" with
a pointer to a dummy character variable to every sscanf statement.

The construct `if (sscanf(string, "%d%c", &number, &dummy) == 1)' succeeds
only if string is a null-terminated number (optionally preceded by some
whitespace characters). If there is some character appended after the number,
sscanf matches "%c", writes the character to the dummy variable and returns 2.
We check the return value for 1 and consequently reject numbers with some
garbage appended.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>

authored by

Mikulas Patocka and committed by
Alasdair G Kergon
31998ef1 0447568f

+44 -25
+5 -3
drivers/md/dm-crypt.c
··· 1415 1415 char *tmp, *cipher, *chainmode, *ivmode, *ivopts, *keycount; 1416 1416 char *cipher_api = NULL; 1417 1417 int cpu, ret = -EINVAL; 1418 + char dummy; 1418 1419 1419 1420 /* Convert to crypto api definition? */ 1420 1421 if (strchr(cipher_in, '(')) { ··· 1437 1436 1438 1437 if (!keycount) 1439 1438 cc->tfms_count = 1; 1440 - else if (sscanf(keycount, "%u", &cc->tfms_count) != 1 || 1439 + else if (sscanf(keycount, "%u%c", &cc->tfms_count, &dummy) != 1 || 1441 1440 !is_power_of_2(cc->tfms_count)) { 1442 1441 ti->error = "Bad cipher key count specification"; 1443 1442 return -EINVAL; ··· 1582 1581 int ret; 1583 1582 struct dm_arg_set as; 1584 1583 const char *opt_string; 1584 + char dummy; 1585 1585 1586 1586 static struct dm_arg _args[] = { 1587 1587 {0, 1, "Invalid number of feature args"}, ··· 1640 1638 } 1641 1639 1642 1640 ret = -EINVAL; 1643 - if (sscanf(argv[2], "%llu", &tmpll) != 1) { 1641 + if (sscanf(argv[2], "%llu%c", &tmpll, &dummy) != 1) { 1644 1642 ti->error = "Invalid iv_offset sector"; 1645 1643 goto bad; 1646 1644 } ··· 1651 1649 goto bad; 1652 1650 } 1653 1651 1654 - if (sscanf(argv[4], "%llu", &tmpll) != 1) { 1652 + if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1) { 1655 1653 ti->error = "Invalid device sector"; 1656 1654 goto bad; 1657 1655 }
+5 -4
drivers/md/dm-delay.c
··· 131 131 { 132 132 struct delay_c *dc; 133 133 unsigned long long tmpll; 134 + char dummy; 134 135 135 136 if (argc != 3 && argc != 6) { 136 137 ti->error = "requires exactly 3 or 6 arguments"; ··· 146 145 147 146 dc->reads = dc->writes = 0; 148 147 149 - if (sscanf(argv[1], "%llu", &tmpll) != 1) { 148 + if (sscanf(argv[1], "%llu%c", &tmpll, &dummy) != 1) { 150 149 ti->error = "Invalid device sector"; 151 150 goto bad; 152 151 } 153 152 dc->start_read = tmpll; 154 153 155 - if (sscanf(argv[2], "%u", &dc->read_delay) != 1) { 154 + if (sscanf(argv[2], "%u%c", &dc->read_delay, &dummy) != 1) { 156 155 ti->error = "Invalid delay"; 157 156 goto bad; 158 157 } ··· 167 166 if (argc == 3) 168 167 goto out; 169 168 170 - if (sscanf(argv[4], "%llu", &tmpll) != 1) { 169 + if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1) { 171 170 ti->error = "Invalid write device sector"; 172 171 goto bad_dev_read; 173 172 } 174 173 dc->start_write = tmpll; 175 174 176 - if (sscanf(argv[5], "%u", &dc->write_delay) != 1) { 175 + if (sscanf(argv[5], "%u%c", &dc->write_delay, &dummy) != 1) { 177 176 ti->error = "Invalid write delay"; 178 177 goto bad_dev_read; 179 178 }
+2 -1
drivers/md/dm-flakey.c
··· 160 160 unsigned long long tmpll; 161 161 struct dm_arg_set as; 162 162 const char *devname; 163 + char dummy; 163 164 164 165 as.argc = argc; 165 166 as.argv = argv; ··· 179 178 180 179 devname = dm_shift_arg(&as); 181 180 182 - if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) { 181 + if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1) { 183 182 ti->error = "Invalid device sector"; 184 183 goto bad; 185 184 }
+3 -2
drivers/md/dm-ioctl.c
··· 880 880 struct hd_geometry geometry; 881 881 unsigned long indata[4]; 882 882 char *geostr = (char *) param + param->data_start; 883 + char dummy; 883 884 884 885 md = find_device(param); 885 886 if (!md) ··· 892 891 goto out; 893 892 } 894 893 895 - x = sscanf(geostr, "%lu %lu %lu %lu", indata, 896 - indata + 1, indata + 2, indata + 3); 894 + x = sscanf(geostr, "%lu %lu %lu %lu%c", indata, 895 + indata + 1, indata + 2, indata + 3, &dummy); 897 896 898 897 if (x != 4) { 899 898 DMWARN("Unable to interpret geometry settings.");
+2 -1
drivers/md/dm-linear.c
··· 29 29 { 30 30 struct linear_c *lc; 31 31 unsigned long long tmp; 32 + char dummy; 32 33 33 34 if (argc != 2) { 34 35 ti->error = "Invalid argument count"; ··· 42 41 return -ENOMEM; 43 42 } 44 43 45 - if (sscanf(argv[1], "%llu", &tmp) != 1) { 44 + if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1) { 46 45 ti->error = "dm-linear: Invalid device sector"; 47 46 goto bad; 48 47 }
+2 -1
drivers/md/dm-log.c
··· 369 369 unsigned int region_count; 370 370 size_t bitset_size, buf_size; 371 371 int r; 372 + char dummy; 372 373 373 374 if (argc < 1 || argc > 2) { 374 375 DMWARN("wrong number of arguments to dirty region log"); ··· 388 387 } 389 388 } 390 389 391 - if (sscanf(argv[0], "%u", &region_size) != 1 || 390 + if (sscanf(argv[0], "%u%c", &region_size, &dummy) != 1 || 392 391 !_check_region_size(ti, region_size)) { 393 392 DMWARN("invalid region size %s", argv[0]); 394 393 return -EINVAL;
+4 -2
drivers/md/dm-mpath.c
··· 1070 1070 struct priority_group *pg; 1071 1071 unsigned pgnum; 1072 1072 unsigned long flags; 1073 + char dummy; 1073 1074 1074 - if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum || 1075 + if (!pgstr || (sscanf(pgstr, "%u%c", &pgnum, &dummy) != 1) || !pgnum || 1075 1076 (pgnum > m->nr_priority_groups)) { 1076 1077 DMWARN("invalid PG number supplied to switch_pg_num"); 1077 1078 return -EINVAL; ··· 1102 1101 { 1103 1102 struct priority_group *pg; 1104 1103 unsigned pgnum; 1104 + char dummy; 1105 1105 1106 - if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum || 1106 + if (!pgstr || (sscanf(pgstr, "%u%c", &pgnum, &dummy) != 1) || !pgnum || 1107 1107 (pgnum > m->nr_priority_groups)) { 1108 1108 DMWARN("invalid PG number supplied to bypass_pg"); 1109 1109 return -EINVAL;
+2 -1
drivers/md/dm-queue-length.c
··· 112 112 struct selector *s = ps->context; 113 113 struct path_info *pi; 114 114 unsigned repeat_count = QL_MIN_IO; 115 + char dummy; 115 116 116 117 /* 117 118 * Arguments: [<repeat_count>] ··· 124 123 return -EINVAL; 125 124 } 126 125 127 - if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) { 126 + if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 128 127 *error = "queue-length ps: invalid repeat count"; 129 128 return -EINVAL; 130 129 }
+8 -4
drivers/md/dm-raid1.c
··· 924 924 unsigned int mirror, char **argv) 925 925 { 926 926 unsigned long long offset; 927 + char dummy; 927 928 928 - if (sscanf(argv[1], "%llu", &offset) != 1) { 929 + if (sscanf(argv[1], "%llu%c", &offset, &dummy) != 1) { 929 930 ti->error = "Invalid offset"; 930 931 return -EINVAL; 931 932 } ··· 954 953 { 955 954 unsigned param_count; 956 955 struct dm_dirty_log *dl; 956 + char dummy; 957 957 958 958 if (argc < 2) { 959 959 ti->error = "Insufficient mirror log arguments"; 960 960 return NULL; 961 961 } 962 962 963 - if (sscanf(argv[1], "%u", &param_count) != 1) { 963 + if (sscanf(argv[1], "%u%c", &param_count, &dummy) != 1) { 964 964 ti->error = "Invalid mirror log argument count"; 965 965 return NULL; 966 966 } ··· 988 986 { 989 987 unsigned num_features; 990 988 struct dm_target *ti = ms->ti; 989 + char dummy; 991 990 992 991 *args_used = 0; 993 992 994 993 if (!argc) 995 994 return 0; 996 995 997 - if (sscanf(argv[0], "%u", &num_features) != 1) { 996 + if (sscanf(argv[0], "%u%c", &num_features, &dummy) != 1) { 998 997 ti->error = "Invalid number of features"; 999 998 return -EINVAL; 1000 999 } ··· 1039 1036 unsigned int nr_mirrors, m, args_used; 1040 1037 struct mirror_set *ms; 1041 1038 struct dm_dirty_log *dl; 1039 + char dummy; 1042 1040 1043 1041 dl = create_dirty_log(ti, argc, argv, &args_used); 1044 1042 if (!dl) ··· 1048 1044 argv += args_used; 1049 1045 argc -= args_used; 1050 1046 1051 - if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 || 1047 + if (!argc || sscanf(argv[0], "%u%c", &nr_mirrors, &dummy) != 1 || 1052 1048 nr_mirrors < 2 || nr_mirrors > DM_KCOPYD_MAX_REGIONS + 1) { 1053 1049 ti->error = "Invalid number of mirrors"; 1054 1050 dm_dirty_log_destroy(dl);
+2 -1
drivers/md/dm-round-robin.c
··· 114 114 struct selector *s = (struct selector *) ps->context; 115 115 struct path_info *pi; 116 116 unsigned repeat_count = RR_MIN_IO; 117 + char dummy; 117 118 118 119 if (argc > 1) { 119 120 *error = "round-robin ps: incorrect number of arguments"; ··· 122 121 } 123 122 124 123 /* First path argument is number of I/Os before switching path */ 125 - if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) { 124 + if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 126 125 *error = "round-robin ps: invalid repeat count"; 127 126 return -EINVAL; 128 127 }
+3 -2
drivers/md/dm-service-time.c
··· 110 110 struct path_info *pi; 111 111 unsigned repeat_count = ST_MIN_IO; 112 112 unsigned relative_throughput = 1; 113 + char dummy; 113 114 114 115 /* 115 116 * Arguments: [<repeat_count> [<relative_throughput>]] ··· 129 128 return -EINVAL; 130 129 } 131 130 132 - if (argc && (sscanf(argv[0], "%u", &repeat_count) != 1)) { 131 + if (argc && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 133 132 *error = "service-time ps: invalid repeat count"; 134 133 return -EINVAL; 135 134 } 136 135 137 136 if ((argc == 2) && 138 - (sscanf(argv[1], "%u", &relative_throughput) != 1 || 137 + (sscanf(argv[1], "%u%c", &relative_throughput, &dummy) != 1 || 139 138 relative_throughput > ST_MAX_RELATIVE_THROUGHPUT)) { 140 139 *error = "service-time ps: invalid relative_throughput value"; 141 140 return -EINVAL;
+2 -1
drivers/md/dm-stripe.c
··· 75 75 unsigned int stripe, char **argv) 76 76 { 77 77 unsigned long long start; 78 + char dummy; 78 79 79 - if (sscanf(argv[1], "%llu", &start) != 1) 80 + if (sscanf(argv[1], "%llu%c", &start, &dummy) != 1) 80 81 return -EINVAL; 81 82 82 83 if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table),
+4 -2
drivers/md/dm-table.c
··· 463 463 struct dm_dev_internal *dd; 464 464 unsigned int major, minor; 465 465 struct dm_table *t = ti->table; 466 + char dummy; 466 467 467 468 BUG_ON(!t); 468 469 469 - if (sscanf(path, "%u:%u", &major, &minor) == 2) { 470 + if (sscanf(path, "%u:%u%c", &major, &minor, &dummy) == 2) { 470 471 /* Extract the major/minor numbers */ 471 472 dev = MKDEV(major, minor); 472 473 if (MAJOR(dev) != major || MINOR(dev) != minor) ··· 842 841 unsigned *value, char **error, unsigned grouped) 843 842 { 844 843 const char *arg_str = dm_shift_arg(arg_set); 844 + char dummy; 845 845 846 846 if (!arg_str || 847 - (sscanf(arg_str, "%u", value) != 1) || 847 + (sscanf(arg_str, "%u%c", value, &dummy) != 1) || 848 848 (*value < arg->min) || 849 849 (*value > arg->max) || 850 850 (grouped && arg_set->argc < *value)) {