aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorPaul Eggert2014-01-19 00:50:53 -0800
committerPaul Eggert2014-01-19 00:50:53 -0800
commitd70efef4a8e5c12db9fe3c305aa9590a91be8467 (patch)
treea1fc8f230c757b69fe96f8b1973cf4da7ea0376d /lib-src
parent9a685cf6bdb1636008acaf84ec8c1b3d3e21f419 (diff)
downloademacs-d70efef4a8e5c12db9fe3c305aa9590a91be8467.tar.gz
emacs-d70efef4a8e5c12db9fe3c305aa9590a91be8467.zip
update-game-score fixes for -m and integer overflow
* update-game-score.c: Include inttypes.h, stdbool.h. (min): New macro, if not already defined. (MAX_SCORES, main): Limit the maximum number of scores only from limits imposed by the underyling platform, instead of the arbitrary value 200. (struct score_entry, main, read_score, write_score): Scores are now intmax_t, not long. (get_user_id): Reject user names containing spaces or newlines, as they would mess up the score file. Allow uids that don't fit in 'long'. Increase the size of the buffer, to avoid overrun in weird cases. (get_prefix, main): Use bool for boolean. (main): Rewrite expr to avoid possibility of signed integer overflow. Don't allow newlines in data, as this would mess up the score file. Check for memory allocation failure when adding the new score, or when unlockint the file. Implement -m. (read_score): Check for integer overflow when reading a score. (read_score) [!HAVE_GETDELIM]: Check for integer overflow when data gets very long. Check only for space to delimit names, since that's what's done in the HAVE_GETDELIM case. (read_scores): New parameter ALLOC. Change counts to ptrdiff_t. All uses changed. Use push_score to add individual scores; that's simpler than repeating its contents. (score_compare_reverse): Simplify. (push_score): New parameter SIZE. Change counts to ptrdiff_t. All uses changed. Check for integer overflow of size calculation. (sort_scores, write_scores): Change counts to ptrdiff_t. (unlock_file): Preserve errno on success, so that storage exhaustion is diagnosed correctly. Fixes: debbugs:16428
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/ChangeLog33
-rw-r--r--lib-src/update-game-score.c221
2 files changed, 169 insertions, 85 deletions
diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog
index dc4ec91c512..50273e2a60a 100644
--- a/lib-src/ChangeLog
+++ b/lib-src/ChangeLog
@@ -1,3 +1,36 @@
12014-01-19 Paul Eggert <eggert@cs.ucla.edu>
2
3 update-game-score fixes for -m and integer overflow (Bug#16428)
4 * update-game-score.c: Include inttypes.h, stdbool.h.
5 (min): New macro, if not already defined.
6 (MAX_SCORES, main): Limit the maximum number of scores only from
7 limits imposed by the underyling platform, instead of the
8 arbitrary value 200.
9 (struct score_entry, main, read_score, write_score):
10 Scores are now intmax_t, not long.
11 (get_user_id): Reject user names containing spaces or newlines,
12 as they would mess up the score file.
13 Allow uids that don't fit in 'long'.
14 Increase the size of the buffer, to avoid overrun in weird cases.
15 (get_prefix, main): Use bool for boolean.
16 (main): Rewrite expr to avoid possibility of signed integer
17 overflow. Don't allow newlines in data, as this would mess up
18 the score file. Check for memory allocation failure when adding
19 the new score, or when unlockint the file. Implement -m.
20 (read_score): Check for integer overflow when reading a score.
21 (read_score) [!HAVE_GETDELIM]: Check for integer overflow when
22 data gets very long. Check only for space to delimit names,
23 since that's what's done in the HAVE_GETDELIM case.
24 (read_scores): New parameter ALLOC. Change counts to ptrdiff_t.
25 All uses changed. Use push_score to add individual scores;
26 that's simpler than repeating its contents.
27 (score_compare_reverse): Simplify.
28 (push_score): New parameter SIZE. Change counts to ptrdiff_t.
29 All uses changed. Check for integer overflow of size calculation.
30 (sort_scores, write_scores): Change counts to ptrdiff_t.
31 (unlock_file): Preserve errno on success, so that storage
32 exhaustion is diagnosed correctly.
33
12014-01-05 Paul Eggert <eggert@cs.ucla.edu> 342014-01-05 Paul Eggert <eggert@cs.ucla.edu>
2 35
3 Spelling fixes. 36 Spelling fixes.
diff --git a/lib-src/update-game-score.c b/lib-src/update-game-score.c
index e2bf93bb1eb..916a351432d 100644
--- a/lib-src/update-game-score.c
+++ b/lib-src/update-game-score.c
@@ -35,7 +35,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
35 35
36#include <unistd.h> 36#include <unistd.h>
37#include <errno.h> 37#include <errno.h>
38#include <inttypes.h>
38#include <limits.h> 39#include <limits.h>
40#include <stdbool.h>
39#include <string.h> 41#include <string.h>
40#include <stdlib.h> 42#include <stdlib.h>
41#include <stdio.h> 43#include <stdio.h>
@@ -50,8 +52,11 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
50#include "ntlib.h" 52#include "ntlib.h"
51#endif 53#endif
52 54
55#ifndef min
56# define min(a,b) ((a) < (b) ? (a) : (b))
57#endif
58
53#define MAX_ATTEMPTS 5 59#define MAX_ATTEMPTS 5
54#define MAX_SCORES 200
55#define MAX_DATA_LEN 1024 60#define MAX_DATA_LEN 1024
56 61
57#ifndef HAVE_DIFFTIME 62#ifndef HAVE_DIFFTIME
@@ -76,18 +81,21 @@ static int unlock_file (const char *filename, void *state);
76 81
77struct score_entry 82struct score_entry
78{ 83{
79 long score; 84 intmax_t score;
80 char *username; 85 char *username;
81 char *data; 86 char *data;
82}; 87};
83 88
89#define MAX_SCORES min (PTRDIFF_MAX, SIZE_MAX / sizeof (struct score_entry))
90
84static int read_scores (const char *filename, struct score_entry **scores, 91static int read_scores (const char *filename, struct score_entry **scores,
85 int *count); 92 ptrdiff_t *count, ptrdiff_t *alloc);
86static int push_score (struct score_entry **scores, int *count, 93static int push_score (struct score_entry **scores, ptrdiff_t *count,
87 int newscore, char *username, char *newdata); 94 ptrdiff_t *size, struct score_entry const *newscore);
88static void sort_scores (struct score_entry *scores, int count, int reverse); 95static void sort_scores (struct score_entry *scores, ptrdiff_t count,
96 bool reverse);
89static int write_scores (const char *filename, 97static int write_scores (const char *filename,
90 const struct score_entry *scores, int count); 98 const struct score_entry *scores, ptrdiff_t count);
91 99
92static _Noreturn void 100static _Noreturn void
93lose (const char *msg) 101lose (const char *msg)
@@ -107,19 +115,19 @@ static char *
107get_user_id (void) 115get_user_id (void)
108{ 116{
109 struct passwd *buf = getpwuid (getuid ()); 117 struct passwd *buf = getpwuid (getuid ());
110 if (!buf) 118 if (!buf || strchr (buf->pw_name, ' ') || strchr (buf->pw_name, '\n'))
111 { 119 {
112 long uid = getuid (); 120 intmax_t uid = getuid ();
113 char *name = malloc (sizeof uid * CHAR_BIT / 3 + 1); 121 char *name = malloc (sizeof uid * CHAR_BIT / 3 + 4);
114 if (name) 122 if (name)
115 sprintf (name, "%ld", uid); 123 sprintf (name, "%"PRIdMAX, uid);
116 return name; 124 return name;
117 } 125 }
118 return buf->pw_name; 126 return buf->pw_name;
119} 127}
120 128
121static const char * 129static const char *
122get_prefix (int running_suid, const char *user_prefix) 130get_prefix (bool running_suid, const char *user_prefix)
123{ 131{
124 if (!running_suid && user_prefix == NULL) 132 if (!running_suid && user_prefix == NULL)
125 lose ("Not using a shared game directory, and no prefix given."); 133 lose ("Not using a shared game directory, and no prefix given.");
@@ -137,14 +145,18 @@ get_prefix (int running_suid, const char *user_prefix)
137int 145int
138main (int argc, char **argv) 146main (int argc, char **argv)
139{ 147{
140 int c, running_suid; 148 int c;
149 bool running_suid;
141 void *lockstate; 150 void *lockstate;
142 char *user_id, *scorefile; 151 char *scorefile;
152 char *nl;
143 const char *prefix, *user_prefix = NULL; 153 const char *prefix, *user_prefix = NULL;
144 struct stat buf; 154 struct stat buf;
145 struct score_entry *scores; 155 struct score_entry *scores;
146 int newscore, scorecount, reverse = 0, max = MAX_SCORES; 156 struct score_entry newscore;
147 char *newdata; 157 bool reverse = false;
158 ptrdiff_t scorecount, scorealloc;
159 ptrdiff_t max_scores = MAX_SCORES;
148 160
149 srand (time (0)); 161 srand (time (0));
150 162
@@ -161,15 +173,18 @@ main (int argc, char **argv)
161 reverse = 1; 173 reverse = 1;
162 break; 174 break;
163 case 'm': 175 case 'm':
164 max = atoi (optarg); 176 {
165 if (max > MAX_SCORES) 177 intmax_t m = strtoimax (optarg, 0, 10);
166 max = MAX_SCORES; 178 if (m < 0)
179 usage (EXIT_FAILURE);
180 max_scores = min (m, MAX_SCORES);
181 }
167 break; 182 break;
168 default: 183 default:
169 usage (EXIT_FAILURE); 184 usage (EXIT_FAILURE);
170 } 185 }
171 186
172 if (optind+3 != argc) 187 if (argc - optind != 3)
173 usage (EXIT_FAILURE); 188 usage (EXIT_FAILURE);
174 189
175 running_suid = (getuid () != geteuid ()); 190 running_suid = (getuid () != geteuid ());
@@ -183,13 +198,18 @@ main (int argc, char **argv)
183 strcpy (scorefile, prefix); 198 strcpy (scorefile, prefix);
184 strcat (scorefile, "/"); 199 strcat (scorefile, "/");
185 strcat (scorefile, argv[optind]); 200 strcat (scorefile, argv[optind]);
186 newscore = atoi (argv[optind+1]);
187 newdata = argv[optind+2];
188 if (strlen (newdata) > MAX_DATA_LEN)
189 newdata[MAX_DATA_LEN] = '\0';
190 201
191 user_id = get_user_id (); 202 newscore.score = strtoimax (argv[optind + 1], 0, 10);
192 if (user_id == NULL) 203
204 newscore.data = argv[optind + 2];
205 if (strlen (newscore.data) > MAX_DATA_LEN)
206 newscore.data[MAX_DATA_LEN] = '\0';
207 nl = strchr (newscore.data, '\n');
208 if (nl)
209 *nl = '\0';
210
211 newscore.username = get_user_id ();
212 if (! newscore.username)
193 lose_syserr ("Couldn't determine user id"); 213 lose_syserr ("Couldn't determine user id");
194 214
195 if (stat (scorefile, &buf) < 0) 215 if (stat (scorefile, &buf) < 0)
@@ -198,29 +218,34 @@ main (int argc, char **argv)
198 if (lock_file (scorefile, &lockstate) < 0) 218 if (lock_file (scorefile, &lockstate) < 0)
199 lose_syserr ("Failed to lock scores file"); 219 lose_syserr ("Failed to lock scores file");
200 220
201 if (read_scores (scorefile, &scores, &scorecount) < 0) 221 if (read_scores (scorefile, &scores, &scorecount, &scorealloc) < 0)
202 { 222 {
203 unlock_file (scorefile, lockstate); 223 unlock_file (scorefile, lockstate);
204 lose_syserr ("Failed to read scores file"); 224 lose_syserr ("Failed to read scores file");
205 } 225 }
206 push_score (&scores, &scorecount, newscore, user_id, newdata); 226 if (push_score (&scores, &scorecount, &scorealloc, &newscore) < 0)
227 {
228 unlock_file (scorefile, lockstate);
229 lose_syserr ("Failed to add score");
230 }
207 sort_scores (scores, scorecount, reverse); 231 sort_scores (scores, scorecount, reverse);
208 /* Limit the number of scores. If we're using reverse sorting, then 232 /* Limit the number of scores. If we're using reverse sorting, then
209 also increment the beginning of the array, to skip over the 233 also increment the beginning of the array, to skip over the
210 *smallest* scores. Otherwise, just decrementing the number of 234 *smallest* scores. Otherwise, just decrementing the number of
211 scores suffices, since the smallest is at the end. */ 235 scores suffices, since the smallest is at the end. */
212 if (scorecount > MAX_SCORES) 236 if (scorecount > max_scores)
213 { 237 {
214 if (reverse) 238 if (reverse)
215 scores += (scorecount - MAX_SCORES); 239 scores += scorecount - max_scores;
216 scorecount = MAX_SCORES; 240 scorecount = max_scores;
217 } 241 }
218 if (write_scores (scorefile, scores, scorecount) < 0) 242 if (write_scores (scorefile, scores, scorecount) < 0)
219 { 243 {
220 unlock_file (scorefile, lockstate); 244 unlock_file (scorefile, lockstate);
221 lose_syserr ("Failed to write scores file"); 245 lose_syserr ("Failed to write scores file");
222 } 246 }
223 unlock_file (scorefile, lockstate); 247 if (unlock_file (scorefile, lockstate) < 0)
248 lose_syserr ("Failed to unlock scores file");
224 exit (EXIT_SUCCESS); 249 exit (EXIT_SUCCESS);
225} 250}
226 251
@@ -234,8 +259,12 @@ read_score (FILE *f, struct score_entry *score)
234 return 1; 259 return 1;
235 for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); ) 260 for (score->score = 0; (c = getc (f)) != EOF && isdigit (c); )
236 { 261 {
262 if (INTMAX_MAX / 10 < score->score)
263 return -1;
237 score->score *= 10; 264 score->score *= 10;
238 score->score += (c-48); 265 if (INTMAX_MAX - (c - '0') < score->score)
266 return -1;
267 score->score += c - '0';
239 } 268 }
240 while ((c = getc (f)) != EOF 269 while ((c = getc (f)) != EOF
241 && isspace (c)) 270 && isspace (c))
@@ -254,18 +283,30 @@ read_score (FILE *f, struct score_entry *score)
254 } 283 }
255#else 284#else
256 { 285 {
257 int unameread = 0; 286 ptrdiff_t unameread = 0;
258 int unamelen = 30; 287 ptrdiff_t unamelen = 30;
259 char *username = malloc (unamelen); 288 char *username = malloc (unamelen);
260 if (!username) 289 if (!username)
261 return -1; 290 return -1;
262 291
263 while ((c = getc (f)) != EOF 292 while ((c = getc (f)) != EOF && c != ' ')
264 && !isspace (c))
265 { 293 {
266 if (unameread >= unamelen-1) 294 if (unameread >= unamelen - 1)
267 if (!(username = realloc (username, unamelen *= 2))) 295 {
268 return -1; 296 ptrdiff_t unamelen_max = min (PTRDIFF_MAX, SIZE_MAX);
297 if (unamelen <= unamelen_max / 2)
298 unamelen *= 2;
299 else if (unamelen < unamelen_max)
300 unamelen = unamelen_max;
301 else
302 {
303 errno = ENOMEM;
304 return -1;
305 }
306 username = realloc (username, unamelen);
307 if (!username)
308 return -1;
309 }
269 username[unameread] = c; 310 username[unameread] = c;
270 unameread++; 311 unameread++;
271 } 312 }
@@ -286,8 +327,8 @@ read_score (FILE *f, struct score_entry *score)
286 } 327 }
287#else 328#else
288 { 329 {
289 int cur = 0; 330 ptrdiff_t cur = 0;
290 int len = 16; 331 ptrdiff_t len = 16;
291 char *buf = malloc (len); 332 char *buf = malloc (len);
292 if (!buf) 333 if (!buf)
293 return -1; 334 return -1;
@@ -296,6 +337,11 @@ read_score (FILE *f, struct score_entry *score)
296 { 337 {
297 if (cur >= len-1) 338 if (cur >= len-1)
298 { 339 {
340 if (min (PTRDIFF_MAX, SIZE_MAX) / 2 < len)
341 {
342 errno = ENOMEM;
343 return -1;
344 }
299 if (!(buf = realloc (buf, len *= 2))) 345 if (!(buf = realloc (buf, len *= 2)))
300 return -1; 346 return -1;
301 } 347 }
@@ -310,35 +356,25 @@ read_score (FILE *f, struct score_entry *score)
310} 356}
311 357
312static int 358static int
313read_scores (const char *filename, struct score_entry **scores, int *count) 359read_scores (const char *filename, struct score_entry **scores,
360 ptrdiff_t *count, ptrdiff_t *alloc)
314{ 361{
315 int readval = -1, scorecount, cursize; 362 int readval = -1;
316 struct score_entry *ret; 363 ptrdiff_t scorecount = 0;
364 ptrdiff_t cursize = 0;
365 struct score_entry *ret = 0;
366 struct score_entry entry;
317 FILE *f = fopen (filename, "r"); 367 FILE *f = fopen (filename, "r");
318 int retval = -1; 368 int retval = -1;
319 if (!f) 369 if (!f)
320 return -1; 370 return -1;
321 scorecount = 0; 371 while ((readval = read_score (f, &entry)) == 0)
322 cursize = 16; 372 if (push_score (&ret, &scorecount, &cursize, &entry) < 0)
323 ret = (struct score_entry *) malloc (sizeof (struct score_entry) * cursize); 373 return -1;
324 if (ret)
325 {
326 while ((readval = read_score (f, &ret[scorecount])) == 0)
327 {
328 scorecount++;
329 if (scorecount >= cursize)
330 {
331 cursize *= 2;
332 ret = (struct score_entry *)
333 realloc (ret, (sizeof (struct score_entry) * cursize));
334 if (!ret)
335 break;
336 }
337 }
338 }
339 if (readval > 0) 374 if (readval > 0)
340 { 375 {
341 *count = scorecount; 376 *count = scorecount;
377 *alloc = cursize;
342 *scores = ret; 378 *scores = ret;
343 retval = 0; 379 retval = 0;
344 } 380 }
@@ -357,40 +393,53 @@ score_compare (const void *a, const void *b)
357static int 393static int
358score_compare_reverse (const void *a, const void *b) 394score_compare_reverse (const void *a, const void *b)
359{ 395{
360 const struct score_entry *sa = (const struct score_entry *) a; 396 return score_compare (b, a);
361 const struct score_entry *sb = (const struct score_entry *) b;
362 return (sa->score > sb->score) - (sa->score < sb->score);
363} 397}
364 398
365int 399int
366push_score (struct score_entry **scores, int *count, int newscore, char *username, char *newdata) 400push_score (struct score_entry **scores, ptrdiff_t *count, ptrdiff_t *size,
401 struct score_entry const *newscore)
367{ 402{
368 struct score_entry *newscores 403 struct score_entry *newscores = *scores;
369 = (struct score_entry *) realloc (*scores, 404 if (*count == *size)
370 sizeof (struct score_entry) * ((*count) + 1)); 405 {
371 if (!newscores) 406 ptrdiff_t newsize = *size;
372 return -1; 407 if (newsize <= 0)
373 newscores[*count].score = newscore; 408 newsize = 1;
374 newscores[*count].username = username; 409 else if (newsize <= MAX_SCORES / 2)
375 newscores[*count].data = newdata; 410 newsize *= 2;
411 else if (newsize < MAX_SCORES)
412 newsize = MAX_SCORES;
413 else
414 {
415 errno = ENOMEM;
416 return -1;
417 }
418 newscores = realloc (newscores, sizeof *newscores * newsize);
419 if (!newscores)
420 return -1;
421 *scores = newscores;
422 *size = newsize;
423 }
424 newscores[*count] = *newscore;
376 (*count) += 1; 425 (*count) += 1;
377 *scores = newscores;
378 return 0; 426 return 0;
379} 427}
380 428
381static void 429static void
382sort_scores (struct score_entry *scores, int count, int reverse) 430sort_scores (struct score_entry *scores, ptrdiff_t count, bool reverse)
383{ 431{
384 qsort (scores, count, sizeof (struct score_entry), 432 qsort (scores, count, sizeof *scores,
385 reverse ? score_compare_reverse : score_compare); 433 reverse ? score_compare_reverse : score_compare);
386} 434}
387 435
388static int 436static int
389write_scores (const char *filename, const struct score_entry *scores, int count) 437write_scores (const char *filename, const struct score_entry *scores,
438 ptrdiff_t count)
390{ 439{
391 int fd; 440 int fd;
392 FILE *f; 441 FILE *f;
393 int i; 442 ptrdiff_t i;
394 char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1); 443 char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1);
395 if (!tempfile) 444 if (!tempfile)
396 return -1; 445 return -1;
@@ -403,8 +452,9 @@ write_scores (const char *filename, const struct score_entry *scores, int count)
403 if (! f) 452 if (! f)
404 return -1; 453 return -1;
405 for (i = 0; i < count; i++) 454 for (i = 0; i < count; i++)
406 if (fprintf (f, "%ld %s %s\n", scores[i].score, scores[i].username, 455 if (fprintf (f, "%"PRIdMAX" %s %s\n",
407 scores[i].data) < 0) 456 scores[i].score, scores[i].username, scores[i].data)
457 < 0)
408 return -1; 458 return -1;
409 fclose (f); 459 fclose (f);
410 if (rename (tempfile, filename) < 0) 460 if (rename (tempfile, filename) < 0)
@@ -459,10 +509,11 @@ static int
459unlock_file (const char *filename, void *state) 509unlock_file (const char *filename, void *state)
460{ 510{
461 char *lockpath = (char *) state; 511 char *lockpath = (char *) state;
462 int ret = unlink (lockpath);
463 int saved_errno = errno; 512 int saved_errno = errno;
513 int ret = unlink (lockpath);
514 int unlink_errno = errno;
464 free (lockpath); 515 free (lockpath);
465 errno = saved_errno; 516 errno = ret < 0 ? unlink_errno : saved_errno;
466 return ret; 517 return ret;
467} 518}
468 519