server-info: fix blind pointer arithmetic

When we're writing out a new objects/info/packs file, we read back the old one to try to keep the ordering the same. When we see a line starting with "P", we expect "P pack-1234..." and blindly jump to "line + 2" to parse the pack name. If we saw a line with _just_ "P" and nothing else, we'd jump past the end of the buffer and start reading arbitrary memory. This shouldn't be a big attack vector, as the files are local to the repository and written by us, but it's clearly worth fixing (we do read remote copies of the file for dumb-http fetches, but using a totally different parser!). Let's instead use skip_prefix() here, which avoids pointer arithmetic altogether. Note that this converts our switch statement to an if/else chain, making it slightly more verbose. But it will also make it easier to do a few follow-on cleanups. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Apr 5, 2019 at 14:13 UTC b83a3089b584f622054e85b9bacbd18014259b7c
1 file changed +12 -10
server-info.c
+12 -10
@@ -112,9 +112,9 @@ static struct pack_info *find_pack_by_name(const char *name)
112 /* Returns non-zero when we detect that the info in the
113 * old file is useless.
114 */
115 -static int parse_pack_def(const char *line, int old_cnt)
115 +static int parse_pack_def(const char *packname, int old_cnt)
116 {
117 - struct pack_info *i = find_pack_by_name(line + 2);
117 + struct pack_info *i = find_pack_by_name(packname);
118 if (i) {
119 i->old_num = old_cnt;
120 return 0;
@@ -139,6 +139,7 @@ static int read_pack_info_file(const char *infofile)
139 return 1; /* nonexistent is not an error. */
140
141 while (fgets(line, sizeof(line), fp)) {
142 + const char *arg;
143 int len = strlen(line);
144 if (len && line[len-1] == '\n')
145 line[--len] = 0;
@@ -146,17 +147,18 @@ static int read_pack_info_file(const char *infofile)
147 if (!len)
148 continue;
149
149 - switch (line[0]) {
150 - case 'P': /* P name */
151 - if (parse_pack_def(line, old_cnt++))
150 + if (skip_prefix(line, "P ", &arg)) {
151 + /* P name */
152 + if (parse_pack_def(arg, old_cnt++))
153 goto out_stale;
153 - break;
154 - case 'D': /* we used to emit D but that was misguided. */
155 - case 'T': /* we used to emit T but nobody uses it. */
154 + } else if (line[0] == 'D') {
155 + /* we used to emit D but that was misguided. */
156 goto out_stale;
157 - default:
157 + } else if (line[0] == 'T') {
158 + /* we used to emit T but nobody uses it. */
159 + goto out_stale;
160 + } else {
161 error("unrecognized: %s", line);
159 - break;
162 }
163 }
164 fclose(fp);