aboutsummaryrefslogtreecommitdiffstats
path: root/fs/compat.c
diff options
context:
space:
mode:
authorOllie Wild <aaw@google.com>2007-07-19 04:48:16 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 13:04:45 -0400
commitb6a2fea39318e43fee84fa7b0b90d68bed92d2ba (patch)
treec9c3619cb2730b5c10c7427b837146bce3d69156 /fs/compat.c
parentbdf4c48af20a3b0f01671799ace345e3d49576da (diff)
mm: variable length argument support
Remove the arg+env limit of MAX_ARG_PAGES by copying the strings directly from the old mm into the new mm. We create the new mm before the binfmt code runs, and place the new stack at the very top of the address space. Once the binfmt code runs and figures out where the stack should be, we move it downwards. It is a bit peculiar in that we have one task with two mm's, one of which is inactive. [a.p.zijlstra@chello.nl: limit stack size] Signed-off-by: Ollie Wild <aaw@google.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: <linux-arch@vger.kernel.org> Cc: Hugh Dickins <hugh@veritas.com> [bunk@stusta.de: unexport bprm_mm_init] Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/compat.c')
-rw-r--r--fs/compat.c128
1 files changed, 53 insertions, 75 deletions
diff --git a/fs/compat.c b/fs/compat.c
index 4db6216e5266..15078ce4c04a 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1257,6 +1257,7 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
1257{ 1257{
1258 struct page *kmapped_page = NULL; 1258 struct page *kmapped_page = NULL;
1259 char *kaddr = NULL; 1259 char *kaddr = NULL;
1260 unsigned long kpos = 0;
1260 int ret; 1261 int ret;
1261 1262
1262 while (argc-- > 0) { 1263 while (argc-- > 0) {
@@ -1265,92 +1266,84 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
1265 unsigned long pos; 1266 unsigned long pos;
1266 1267
1267 if (get_user(str, argv+argc) || 1268 if (get_user(str, argv+argc) ||
1268 !(len = strnlen_user(compat_ptr(str), bprm->p))) { 1269 !(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) {
1269 ret = -EFAULT; 1270 ret = -EFAULT;
1270 goto out; 1271 goto out;
1271 } 1272 }
1272 1273
1273 if (bprm->p < len) { 1274 if (len > MAX_ARG_STRLEN) {
1274 ret = -E2BIG; 1275 ret = -E2BIG;
1275 goto out; 1276 goto out;
1276 } 1277 }
1277 1278
1278 bprm->p -= len; 1279 /* We're going to work our way backwords. */
1279 /* XXX: add architecture specific overflow check here. */
1280 pos = bprm->p; 1280 pos = bprm->p;
1281 str += len;
1282 bprm->p -= len;
1281 1283
1282 while (len > 0) { 1284 while (len > 0) {
1283 int i, new, err;
1284 int offset, bytes_to_copy; 1285 int offset, bytes_to_copy;
1285 struct page *page;
1286 1286
1287 offset = pos % PAGE_SIZE; 1287 offset = pos % PAGE_SIZE;
1288 i = pos/PAGE_SIZE; 1288 if (offset == 0)
1289 page = bprm->page[i]; 1289 offset = PAGE_SIZE;
1290 new = 0; 1290
1291 if (!page) { 1291 bytes_to_copy = offset;
1292 page = alloc_page(GFP_HIGHUSER); 1292 if (bytes_to_copy > len)
1293 bprm->page[i] = page; 1293 bytes_to_copy = len;
1294 if (!page) { 1294
1295 ret = -ENOMEM; 1295 offset -= bytes_to_copy;
1296 pos -= bytes_to_copy;
1297 str -= bytes_to_copy;
1298 len -= bytes_to_copy;
1299
1300 if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
1301 struct page *page;
1302
1303#ifdef CONFIG_STACK_GROWSUP
1304 ret = expand_stack_downwards(bprm->vma, pos);
1305 if (ret < 0) {
1306 /* We've exceed the stack rlimit. */
1307 ret = -E2BIG;
1308 goto out;
1309 }
1310#endif
1311 ret = get_user_pages(current, bprm->mm, pos,
1312 1, 1, 1, &page, NULL);
1313 if (ret <= 0) {
1314 /* We've exceed the stack rlimit. */
1315 ret = -E2BIG;
1296 goto out; 1316 goto out;
1297 } 1317 }
1298 new = 1;
1299 }
1300 1318
1301 if (page != kmapped_page) { 1319 if (kmapped_page) {
1302 if (kmapped_page) 1320 flush_kernel_dcache_page(kmapped_page);
1303 kunmap(kmapped_page); 1321 kunmap(kmapped_page);
1322 put_page(kmapped_page);
1323 }
1304 kmapped_page = page; 1324 kmapped_page = page;
1305 kaddr = kmap(kmapped_page); 1325 kaddr = kmap(kmapped_page);
1326 kpos = pos & PAGE_MASK;
1327 flush_cache_page(bprm->vma, kpos,
1328 page_to_pfn(kmapped_page));
1306 } 1329 }
1307 if (new && offset) 1330 if (copy_from_user(kaddr+offset, compat_ptr(str),
1308 memset(kaddr, 0, offset); 1331 bytes_to_copy)) {
1309 bytes_to_copy = PAGE_SIZE - offset;
1310 if (bytes_to_copy > len) {
1311 bytes_to_copy = len;
1312 if (new)
1313 memset(kaddr+offset+len, 0,
1314 PAGE_SIZE-offset-len);
1315 }
1316 err = copy_from_user(kaddr+offset, compat_ptr(str),
1317 bytes_to_copy);
1318 if (err) {
1319 ret = -EFAULT; 1332 ret = -EFAULT;
1320 goto out; 1333 goto out;
1321 } 1334 }
1322
1323 pos += bytes_to_copy;
1324 str += bytes_to_copy;
1325 len -= bytes_to_copy;
1326 } 1335 }
1327 } 1336 }
1328 ret = 0; 1337 ret = 0;
1329out: 1338out:
1330 if (kmapped_page) 1339 if (kmapped_page) {
1340 flush_kernel_dcache_page(kmapped_page);
1331 kunmap(kmapped_page); 1341 kunmap(kmapped_page);
1332 return ret; 1342 put_page(kmapped_page);
1333}
1334
1335#ifdef CONFIG_MMU
1336
1337#define free_arg_pages(bprm) do { } while (0)
1338
1339#else
1340
1341static inline void free_arg_pages(struct linux_binprm *bprm)
1342{
1343 int i;
1344
1345 for (i = 0; i < MAX_ARG_PAGES; i++) {
1346 if (bprm->page[i])
1347 __free_page(bprm->page[i]);
1348 bprm->page[i] = NULL;
1349 } 1343 }
1344 return ret;
1350} 1345}
1351 1346
1352#endif /* CONFIG_MMU */
1353
1354/* 1347/*
1355 * compat_do_execve() is mostly a copy of do_execve(), with the exception 1348 * compat_do_execve() is mostly a copy of do_execve(), with the exception
1356 * that it processes 32 bit argv and envp pointers. 1349 * that it processes 32 bit argv and envp pointers.
@@ -1363,7 +1356,6 @@ int compat_do_execve(char * filename,
1363 struct linux_binprm *bprm; 1356 struct linux_binprm *bprm;
1364 struct file *file; 1357 struct file *file;
1365 int retval; 1358 int retval;
1366 int i;
1367 1359
1368 retval = -ENOMEM; 1360 retval = -ENOMEM;
1369 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); 1361 bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
@@ -1377,24 +1369,19 @@ int compat_do_execve(char * filename,
1377 1369
1378 sched_exec(); 1370 sched_exec();
1379 1371
1380 bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
1381 bprm->file = file; 1372 bprm->file = file;
1382 bprm->filename = filename; 1373 bprm->filename = filename;
1383 bprm->interp = filename; 1374 bprm->interp = filename;
1384 bprm->mm = mm_alloc();
1385 retval = -ENOMEM;
1386 if (!bprm->mm)
1387 goto out_file;
1388 1375
1389 retval = init_new_context(current, bprm->mm); 1376 retval = bprm_mm_init(bprm);
1390 if (retval < 0) 1377 if (retval)
1391 goto out_mm; 1378 goto out_file;
1392 1379
1393 bprm->argc = compat_count(argv, bprm->p / sizeof(compat_uptr_t)); 1380 bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
1394 if ((retval = bprm->argc) < 0) 1381 if ((retval = bprm->argc) < 0)
1395 goto out_mm; 1382 goto out_mm;
1396 1383
1397 bprm->envc = compat_count(envp, bprm->p / sizeof(compat_uptr_t)); 1384 bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
1398 if ((retval = bprm->envc) < 0) 1385 if ((retval = bprm->envc) < 0)
1399 goto out_mm; 1386 goto out_mm;
1400 1387
@@ -1421,8 +1408,6 @@ int compat_do_execve(char * filename,
1421 1408
1422 retval = search_binary_handler(bprm, regs); 1409 retval = search_binary_handler(bprm, regs);
1423 if (retval >= 0) { 1410 if (retval >= 0) {
1424 free_arg_pages(bprm);
1425
1426 /* execve success */ 1411 /* execve success */
1427 security_bprm_free(bprm); 1412 security_bprm_free(bprm);
1428 acct_update_integrals(current); 1413 acct_update_integrals(current);
@@ -1431,19 +1416,12 @@ int compat_do_execve(char * filename,
1431 } 1416 }
1432 1417
1433out: 1418out:
1434 /* Something went wrong, return the inode and free the argument pages*/
1435 for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
1436 struct page * page = bprm->page[i];
1437 if (page)
1438 __free_page(page);
1439 }
1440
1441 if (bprm->security) 1419 if (bprm->security)
1442 security_bprm_free(bprm); 1420 security_bprm_free(bprm);
1443 1421
1444out_mm: 1422out_mm:
1445 if (bprm->mm) 1423 if (bprm->mm)
1446 mmdrop(bprm->mm); 1424 mmput(bprm->mm);
1447 1425
1448out_file: 1426out_file:
1449 if (bprm->file) { 1427 if (bprm->file) {