diff options
author | Patrick McHardy <kaber@trash.net> | 2006-09-20 15:05:37 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 18:20:01 -0400 |
commit | 9fa492cdc160cd27ce1046cb36f47d3b2b1efa21 (patch) | |
tree | 6ecb2a92a87523af2a1f7236f0bca456ca0677f3 /net | |
parent | 79030ed07de673e8451a03aecb9ada9f4d75d491 (diff) |
[NETFILTER]: x_tables: simplify compat API
Split the xt_compat_match/xt_compat_target into smaller type-safe functions
performing just one operation. Handle all alignment and size-related
conversions centrally in these function instead of requiring each module to
implement a full-blown conversion function. Replace ->compat callback by
->compat_from_user and ->compat_to_user callbacks, responsible for
converting just a single private structure.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 115 | ||||
-rw-r--r-- | net/netfilter/x_tables.c | 192 |
2 files changed, 133 insertions, 174 deletions
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 673581db986e..800067d69a9a 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -942,73 +942,28 @@ static short compat_calc_jump(u_int16_t offset) | |||
942 | return delta; | 942 | return delta; |
943 | } | 943 | } |
944 | 944 | ||
945 | struct compat_ipt_standard_target | 945 | static void compat_standard_from_user(void *dst, void *src) |
946 | { | 946 | { |
947 | struct compat_xt_entry_target target; | 947 | int v = *(compat_int_t *)src; |
948 | compat_int_t verdict; | ||
949 | }; | ||
950 | |||
951 | struct compat_ipt_standard | ||
952 | { | ||
953 | struct compat_ipt_entry entry; | ||
954 | struct compat_ipt_standard_target target; | ||
955 | }; | ||
956 | 948 | ||
957 | #define IPT_ST_LEN XT_ALIGN(sizeof(struct ipt_standard_target)) | 949 | if (v > 0) |
958 | #define IPT_ST_COMPAT_LEN COMPAT_XT_ALIGN(sizeof(struct compat_ipt_standard_target)) | 950 | v += compat_calc_jump(v); |
959 | #define IPT_ST_OFFSET (IPT_ST_LEN - IPT_ST_COMPAT_LEN) | 951 | memcpy(dst, &v, sizeof(v)); |
952 | } | ||
960 | 953 | ||
961 | static int compat_ipt_standard_fn(void *target, | 954 | static int compat_standard_to_user(void __user *dst, void *src) |
962 | void **dstptr, int *size, int convert) | ||
963 | { | 955 | { |
964 | struct compat_ipt_standard_target compat_st, *pcompat_st; | 956 | compat_int_t cv = *(int *)src; |
965 | struct ipt_standard_target st, *pst; | ||
966 | int ret; | ||
967 | 957 | ||
968 | ret = 0; | 958 | if (cv > 0) |
969 | switch (convert) { | 959 | cv -= compat_calc_jump(cv); |
970 | case COMPAT_TO_USER: | 960 | return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0; |
971 | pst = target; | ||
972 | memcpy(&compat_st.target, &pst->target, | ||
973 | sizeof(compat_st.target)); | ||
974 | compat_st.verdict = pst->verdict; | ||
975 | if (compat_st.verdict > 0) | ||
976 | compat_st.verdict -= | ||
977 | compat_calc_jump(compat_st.verdict); | ||
978 | compat_st.target.u.user.target_size = IPT_ST_COMPAT_LEN; | ||
979 | if (copy_to_user(*dstptr, &compat_st, IPT_ST_COMPAT_LEN)) | ||
980 | ret = -EFAULT; | ||
981 | *size -= IPT_ST_OFFSET; | ||
982 | *dstptr += IPT_ST_COMPAT_LEN; | ||
983 | break; | ||
984 | case COMPAT_FROM_USER: | ||
985 | pcompat_st = target; | ||
986 | memcpy(&st.target, &pcompat_st->target, IPT_ST_COMPAT_LEN); | ||
987 | st.verdict = pcompat_st->verdict; | ||
988 | if (st.verdict > 0) | ||
989 | st.verdict += compat_calc_jump(st.verdict); | ||
990 | st.target.u.user.target_size = IPT_ST_LEN; | ||
991 | memcpy(*dstptr, &st, IPT_ST_LEN); | ||
992 | *size += IPT_ST_OFFSET; | ||
993 | *dstptr += IPT_ST_LEN; | ||
994 | break; | ||
995 | case COMPAT_CALC_SIZE: | ||
996 | *size += IPT_ST_OFFSET; | ||
997 | break; | ||
998 | default: | ||
999 | ret = -ENOPROTOOPT; | ||
1000 | break; | ||
1001 | } | ||
1002 | return ret; | ||
1003 | } | 961 | } |
1004 | 962 | ||
1005 | static inline int | 963 | static inline int |
1006 | compat_calc_match(struct ipt_entry_match *m, int * size) | 964 | compat_calc_match(struct ipt_entry_match *m, int * size) |
1007 | { | 965 | { |
1008 | if (m->u.kernel.match->compat) | 966 | *size += xt_compat_match_offset(m->u.kernel.match); |
1009 | m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE); | ||
1010 | else | ||
1011 | xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE); | ||
1012 | return 0; | 967 | return 0; |
1013 | } | 968 | } |
1014 | 969 | ||
@@ -1023,10 +978,7 @@ static int compat_calc_entry(struct ipt_entry *e, struct xt_table_info *info, | |||
1023 | entry_offset = (void *)e - base; | 978 | entry_offset = (void *)e - base; |
1024 | IPT_MATCH_ITERATE(e, compat_calc_match, &off); | 979 | IPT_MATCH_ITERATE(e, compat_calc_match, &off); |
1025 | t = ipt_get_target(e); | 980 | t = ipt_get_target(e); |
1026 | if (t->u.kernel.target->compat) | 981 | off += xt_compat_target_offset(t->u.kernel.target); |
1027 | t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE); | ||
1028 | else | ||
1029 | xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE); | ||
1030 | newinfo->size -= off; | 982 | newinfo->size -= off; |
1031 | ret = compat_add_offset(entry_offset, off); | 983 | ret = compat_add_offset(entry_offset, off); |
1032 | if (ret) | 984 | if (ret) |
@@ -1412,17 +1364,13 @@ struct compat_ipt_replace { | |||
1412 | }; | 1364 | }; |
1413 | 1365 | ||
1414 | static inline int compat_copy_match_to_user(struct ipt_entry_match *m, | 1366 | static inline int compat_copy_match_to_user(struct ipt_entry_match *m, |
1415 | void __user **dstptr, compat_uint_t *size) | 1367 | void * __user *dstptr, compat_uint_t *size) |
1416 | { | 1368 | { |
1417 | if (m->u.kernel.match->compat) | 1369 | return xt_compat_match_to_user(m, dstptr, size); |
1418 | return m->u.kernel.match->compat(m, dstptr, size, | ||
1419 | COMPAT_TO_USER); | ||
1420 | else | ||
1421 | return xt_compat_match(m, dstptr, size, COMPAT_TO_USER); | ||
1422 | } | 1370 | } |
1423 | 1371 | ||
1424 | static int compat_copy_entry_to_user(struct ipt_entry *e, | 1372 | static int compat_copy_entry_to_user(struct ipt_entry *e, |
1425 | void __user **dstptr, compat_uint_t *size) | 1373 | void * __user *dstptr, compat_uint_t *size) |
1426 | { | 1374 | { |
1427 | struct ipt_entry_target __user *t; | 1375 | struct ipt_entry_target __user *t; |
1428 | struct compat_ipt_entry __user *ce; | 1376 | struct compat_ipt_entry __user *ce; |
@@ -1442,11 +1390,7 @@ static int compat_copy_entry_to_user(struct ipt_entry *e, | |||
1442 | if (ret) | 1390 | if (ret) |
1443 | goto out; | 1391 | goto out; |
1444 | t = ipt_get_target(e); | 1392 | t = ipt_get_target(e); |
1445 | if (t->u.kernel.target->compat) | 1393 | ret = xt_compat_target_to_user(t, dstptr, size); |
1446 | ret = t->u.kernel.target->compat(t, dstptr, size, | ||
1447 | COMPAT_TO_USER); | ||
1448 | else | ||
1449 | ret = xt_compat_target(t, dstptr, size, COMPAT_TO_USER); | ||
1450 | if (ret) | 1394 | if (ret) |
1451 | goto out; | 1395 | goto out; |
1452 | ret = -EFAULT; | 1396 | ret = -EFAULT; |
@@ -1478,11 +1422,7 @@ compat_check_calc_match(struct ipt_entry_match *m, | |||
1478 | return match ? PTR_ERR(match) : -ENOENT; | 1422 | return match ? PTR_ERR(match) : -ENOENT; |
1479 | } | 1423 | } |
1480 | m->u.kernel.match = match; | 1424 | m->u.kernel.match = match; |
1481 | 1425 | *size += xt_compat_match_offset(match); | |
1482 | if (m->u.kernel.match->compat) | ||
1483 | m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE); | ||
1484 | else | ||
1485 | xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE); | ||
1486 | 1426 | ||
1487 | (*i)++; | 1427 | (*i)++; |
1488 | return 0; | 1428 | return 0; |
@@ -1543,10 +1483,7 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1543 | } | 1483 | } |
1544 | t->u.kernel.target = target; | 1484 | t->u.kernel.target = target; |
1545 | 1485 | ||
1546 | if (t->u.kernel.target->compat) | 1486 | off += xt_compat_target_offset(target); |
1547 | t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE); | ||
1548 | else | ||
1549 | xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE); | ||
1550 | *size += off; | 1487 | *size += off; |
1551 | ret = compat_add_offset(entry_offset, off); | 1488 | ret = compat_add_offset(entry_offset, off); |
1552 | if (ret) | 1489 | if (ret) |
@@ -1584,10 +1521,7 @@ static inline int compat_copy_match_from_user(struct ipt_entry_match *m, | |||
1584 | 1521 | ||
1585 | dm = (struct ipt_entry_match *)*dstptr; | 1522 | dm = (struct ipt_entry_match *)*dstptr; |
1586 | match = m->u.kernel.match; | 1523 | match = m->u.kernel.match; |
1587 | if (match->compat) | 1524 | xt_compat_match_from_user(m, dstptr, size); |
1588 | match->compat(m, dstptr, size, COMPAT_FROM_USER); | ||
1589 | else | ||
1590 | xt_compat_match(m, dstptr, size, COMPAT_FROM_USER); | ||
1591 | 1525 | ||
1592 | ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm), | 1526 | ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm), |
1593 | name, hookmask, ip->proto, | 1527 | name, hookmask, ip->proto, |
@@ -1635,10 +1569,7 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1635 | de->target_offset = e->target_offset - (origsize - *size); | 1569 | de->target_offset = e->target_offset - (origsize - *size); |
1636 | t = ipt_get_target(e); | 1570 | t = ipt_get_target(e); |
1637 | target = t->u.kernel.target; | 1571 | target = t->u.kernel.target; |
1638 | if (target->compat) | 1572 | xt_compat_target_from_user(t, dstptr, size); |
1639 | target->compat(t, dstptr, size, COMPAT_FROM_USER); | ||
1640 | else | ||
1641 | xt_compat_target(t, dstptr, size, COMPAT_FROM_USER); | ||
1642 | 1573 | ||
1643 | de->next_offset = e->next_offset - (origsize - *size); | 1574 | de->next_offset = e->next_offset - (origsize - *size); |
1644 | for (h = 0; h < NF_IP_NUMHOOKS; h++) { | 1575 | for (h = 0; h < NF_IP_NUMHOOKS; h++) { |
@@ -2205,7 +2136,9 @@ static struct ipt_target ipt_standard_target = { | |||
2205 | .targetsize = sizeof(int), | 2136 | .targetsize = sizeof(int), |
2206 | .family = AF_INET, | 2137 | .family = AF_INET, |
2207 | #ifdef CONFIG_COMPAT | 2138 | #ifdef CONFIG_COMPAT |
2208 | .compat = &compat_ipt_standard_fn, | 2139 | .compatsize = sizeof(compat_int_t), |
2140 | .compat_from_user = compat_standard_from_user, | ||
2141 | .compat_to_user = compat_standard_to_user, | ||
2209 | #endif | 2142 | #endif |
2210 | }; | 2143 | }; |
2211 | 2144 | ||
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index be7baf4f6846..58522fc65d33 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c | |||
@@ -333,52 +333,65 @@ int xt_check_match(const struct xt_match *match, unsigned short family, | |||
333 | EXPORT_SYMBOL_GPL(xt_check_match); | 333 | EXPORT_SYMBOL_GPL(xt_check_match); |
334 | 334 | ||
335 | #ifdef CONFIG_COMPAT | 335 | #ifdef CONFIG_COMPAT |
336 | int xt_compat_match(void *match, void **dstptr, int *size, int convert) | 336 | int xt_compat_match_offset(struct xt_match *match) |
337 | { | 337 | { |
338 | struct xt_match *m; | 338 | u_int16_t csize = match->compatsize ? : match->matchsize; |
339 | struct compat_xt_entry_match *pcompat_m; | 339 | return XT_ALIGN(match->matchsize) - COMPAT_XT_ALIGN(csize); |
340 | struct xt_entry_match *pm; | 340 | } |
341 | u_int16_t msize; | 341 | EXPORT_SYMBOL_GPL(xt_compat_match_offset); |
342 | int off, ret; | ||
343 | 342 | ||
344 | ret = 0; | 343 | void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, |
345 | m = ((struct xt_entry_match *)match)->u.kernel.match; | 344 | int *size) |
346 | off = XT_ALIGN(m->matchsize) - COMPAT_XT_ALIGN(m->matchsize); | 345 | { |
347 | switch (convert) { | 346 | struct xt_match *match = m->u.kernel.match; |
348 | case COMPAT_TO_USER: | 347 | struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m; |
349 | pm = (struct xt_entry_match *)match; | 348 | int pad, off = xt_compat_match_offset(match); |
350 | msize = pm->u.user.match_size; | 349 | u_int16_t msize = cm->u.user.match_size; |
351 | if (copy_to_user(*dstptr, pm, msize)) { | 350 | |
352 | ret = -EFAULT; | 351 | m = *dstptr; |
353 | break; | 352 | memcpy(m, cm, sizeof(*cm)); |
354 | } | 353 | if (match->compat_from_user) |
355 | msize -= off; | 354 | match->compat_from_user(m->data, cm->data); |
356 | if (put_user(msize, (u_int16_t *)*dstptr)) | 355 | else |
357 | ret = -EFAULT; | 356 | memcpy(m->data, cm->data, msize - sizeof(*cm)); |
358 | *size -= off; | 357 | pad = XT_ALIGN(match->matchsize) - match->matchsize; |
359 | *dstptr += msize; | 358 | if (pad > 0) |
360 | break; | 359 | memset(m->data + match->matchsize, 0, pad); |
361 | case COMPAT_FROM_USER: | 360 | |
362 | pcompat_m = (struct compat_xt_entry_match *)match; | 361 | msize += off; |
363 | pm = (struct xt_entry_match *)*dstptr; | 362 | m->u.user.match_size = msize; |
364 | msize = pcompat_m->u.user.match_size; | 363 | |
365 | memcpy(pm, pcompat_m, msize); | 364 | *size += off; |
366 | msize += off; | 365 | *dstptr += msize; |
367 | pm->u.user.match_size = msize; | 366 | } |
368 | *size += off; | 367 | EXPORT_SYMBOL_GPL(xt_compat_match_from_user); |
369 | *dstptr += msize; | 368 | |
370 | break; | 369 | int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr, |
371 | case COMPAT_CALC_SIZE: | 370 | int *size) |
372 | *size += off; | 371 | { |
373 | break; | 372 | struct xt_match *match = m->u.kernel.match; |
374 | default: | 373 | struct compat_xt_entry_match __user *cm = *dstptr; |
375 | ret = -ENOPROTOOPT; | 374 | int off = xt_compat_match_offset(match); |
376 | break; | 375 | u_int16_t msize = m->u.user.match_size - off; |
376 | |||
377 | if (copy_to_user(cm, m, sizeof(*cm)) || | ||
378 | put_user(msize, &cm->u.user.match_size)) | ||
379 | return -EFAULT; | ||
380 | |||
381 | if (match->compat_to_user) { | ||
382 | if (match->compat_to_user((void __user *)cm->data, m->data)) | ||
383 | return -EFAULT; | ||
384 | } else { | ||
385 | if (copy_to_user(cm->data, m->data, msize - sizeof(*cm))) | ||
386 | return -EFAULT; | ||
377 | } | 387 | } |
378 | return ret; | 388 | |
389 | *size -= off; | ||
390 | *dstptr += msize; | ||
391 | return 0; | ||
379 | } | 392 | } |
380 | EXPORT_SYMBOL_GPL(xt_compat_match); | 393 | EXPORT_SYMBOL_GPL(xt_compat_match_to_user); |
381 | #endif | 394 | #endif /* CONFIG_COMPAT */ |
382 | 395 | ||
383 | int xt_check_target(const struct xt_target *target, unsigned short family, | 396 | int xt_check_target(const struct xt_target *target, unsigned short family, |
384 | unsigned int size, const char *table, unsigned int hook_mask, | 397 | unsigned int size, const char *table, unsigned int hook_mask, |
@@ -410,51 +423,64 @@ int xt_check_target(const struct xt_target *target, unsigned short family, | |||
410 | EXPORT_SYMBOL_GPL(xt_check_target); | 423 | EXPORT_SYMBOL_GPL(xt_check_target); |
411 | 424 | ||
412 | #ifdef CONFIG_COMPAT | 425 | #ifdef CONFIG_COMPAT |
413 | int xt_compat_target(void *target, void **dstptr, int *size, int convert) | 426 | int xt_compat_target_offset(struct xt_target *target) |
414 | { | 427 | { |
415 | struct xt_target *t; | 428 | u_int16_t csize = target->compatsize ? : target->targetsize; |
416 | struct compat_xt_entry_target *pcompat; | 429 | return XT_ALIGN(target->targetsize) - COMPAT_XT_ALIGN(csize); |
417 | struct xt_entry_target *pt; | 430 | } |
418 | u_int16_t tsize; | 431 | EXPORT_SYMBOL_GPL(xt_compat_target_offset); |
419 | int off, ret; | ||
420 | 432 | ||
421 | ret = 0; | 433 | void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, |
422 | t = ((struct xt_entry_target *)target)->u.kernel.target; | 434 | int *size) |
423 | off = XT_ALIGN(t->targetsize) - COMPAT_XT_ALIGN(t->targetsize); | 435 | { |
424 | switch (convert) { | 436 | struct xt_target *target = t->u.kernel.target; |
425 | case COMPAT_TO_USER: | 437 | struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t; |
426 | pt = (struct xt_entry_target *)target; | 438 | int pad, off = xt_compat_target_offset(target); |
427 | tsize = pt->u.user.target_size; | 439 | u_int16_t tsize = ct->u.user.target_size; |
428 | if (copy_to_user(*dstptr, pt, tsize)) { | 440 | |
429 | ret = -EFAULT; | 441 | t = *dstptr; |
430 | break; | 442 | memcpy(t, ct, sizeof(*ct)); |
431 | } | 443 | if (target->compat_from_user) |
432 | tsize -= off; | 444 | target->compat_from_user(t->data, ct->data); |
433 | if (put_user(tsize, (u_int16_t *)*dstptr)) | 445 | else |
434 | ret = -EFAULT; | 446 | memcpy(t->data, ct->data, tsize - sizeof(*ct)); |
435 | *size -= off; | 447 | pad = XT_ALIGN(target->targetsize) - target->targetsize; |
436 | *dstptr += tsize; | 448 | if (pad > 0) |
437 | break; | 449 | memset(t->data + target->targetsize, 0, pad); |
438 | case COMPAT_FROM_USER: | 450 | |
439 | pcompat = (struct compat_xt_entry_target *)target; | 451 | tsize += off; |
440 | pt = (struct xt_entry_target *)*dstptr; | 452 | t->u.user.target_size = tsize; |
441 | tsize = pcompat->u.user.target_size; | 453 | |
442 | memcpy(pt, pcompat, tsize); | 454 | *size += off; |
443 | tsize += off; | 455 | *dstptr += tsize; |
444 | pt->u.user.target_size = tsize; | 456 | } |
445 | *size += off; | 457 | EXPORT_SYMBOL_GPL(xt_compat_target_from_user); |
446 | *dstptr += tsize; | 458 | |
447 | break; | 459 | int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr, |
448 | case COMPAT_CALC_SIZE: | 460 | int *size) |
449 | *size += off; | 461 | { |
450 | break; | 462 | struct xt_target *target = t->u.kernel.target; |
451 | default: | 463 | struct compat_xt_entry_target __user *ct = *dstptr; |
452 | ret = -ENOPROTOOPT; | 464 | int off = xt_compat_target_offset(target); |
453 | break; | 465 | u_int16_t tsize = t->u.user.target_size - off; |
466 | |||
467 | if (copy_to_user(ct, t, sizeof(*ct)) || | ||
468 | put_user(tsize, &ct->u.user.target_size)) | ||
469 | return -EFAULT; | ||
470 | |||
471 | if (target->compat_to_user) { | ||
472 | if (target->compat_to_user((void __user *)ct->data, t->data)) | ||
473 | return -EFAULT; | ||
474 | } else { | ||
475 | if (copy_to_user(ct->data, t->data, tsize - sizeof(*ct))) | ||
476 | return -EFAULT; | ||
454 | } | 477 | } |
455 | return ret; | 478 | |
479 | *size -= off; | ||
480 | *dstptr += tsize; | ||
481 | return 0; | ||
456 | } | 482 | } |
457 | EXPORT_SYMBOL_GPL(xt_compat_target); | 483 | EXPORT_SYMBOL_GPL(xt_compat_target_to_user); |
458 | #endif | 484 | #endif |
459 | 485 | ||
460 | struct xt_table_info *xt_alloc_table_info(unsigned int size) | 486 | struct xt_table_info *xt_alloc_table_info(unsigned int size) |