diff options
| author | Florian Westphal <fw@strlen.de> | 2010-02-04 12:38:53 -0500 |
|---|---|---|
| committer | Florian Westphal <fw@strlen.de> | 2010-02-16 11:25:21 -0500 |
| commit | e788759f44b29e5b1bc27a265dece7dcfa4234af (patch) | |
| tree | be90c201d4565e5814f19e6a4b487cfb62e44d9c | |
| parent | 3e5e524ffb5fcf2447eb5dd9f8e54ad22dd9baa7 (diff) | |
netfilter: ebtables: split do_replace into two functions
once CONFIG_COMPAT support is merged this allows
to call do_replace_finish() after doing the CONFIG_COMPAT conversion
instead of copy & pasting this.
Signed-off-by: Florian Westphal <fw@strlen.de>
| -rw-r--r-- | net/bridge/netfilter/ebtables.c | 136 |
1 files changed, 71 insertions, 65 deletions
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 4370e9680487..a707dbdc0327 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c | |||
| @@ -959,91 +959,45 @@ static void get_counters(const struct ebt_counter *oldcounters, | |||
| 959 | } | 959 | } |
| 960 | } | 960 | } |
| 961 | 961 | ||
| 962 | /* replace the table */ | 962 | static int do_replace_finish(struct net *net, struct ebt_replace *repl, |
| 963 | static int do_replace(struct net *net, const void __user *user, | 963 | struct ebt_table_info *newinfo) |
| 964 | unsigned int len) | ||
| 965 | { | 964 | { |
| 966 | int ret, i, countersize; | 965 | int ret, i; |
| 967 | struct ebt_table_info *newinfo; | ||
| 968 | struct ebt_replace tmp; | ||
| 969 | struct ebt_table *t; | ||
| 970 | struct ebt_counter *counterstmp = NULL; | 966 | struct ebt_counter *counterstmp = NULL; |
| 971 | /* used to be able to unlock earlier */ | 967 | /* used to be able to unlock earlier */ |
| 972 | struct ebt_table_info *table; | 968 | struct ebt_table_info *table; |
| 973 | 969 | struct ebt_table *t; | |
| 974 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
| 975 | return -EFAULT; | ||
| 976 | |||
| 977 | if (len != sizeof(tmp) + tmp.entries_size) { | ||
| 978 | BUGPRINT("Wrong len argument\n"); | ||
| 979 | return -EINVAL; | ||
| 980 | } | ||
| 981 | |||
| 982 | if (tmp.entries_size == 0) { | ||
| 983 | BUGPRINT("Entries_size never zero\n"); | ||
| 984 | return -EINVAL; | ||
| 985 | } | ||
| 986 | /* overflow check */ | ||
| 987 | if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / NR_CPUS - | ||
| 988 | SMP_CACHE_BYTES) / sizeof(struct ebt_counter)) | ||
| 989 | return -ENOMEM; | ||
| 990 | if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) | ||
| 991 | return -ENOMEM; | ||
| 992 | |||
| 993 | countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids; | ||
| 994 | newinfo = vmalloc(sizeof(*newinfo) + countersize); | ||
| 995 | if (!newinfo) | ||
| 996 | return -ENOMEM; | ||
| 997 | |||
| 998 | if (countersize) | ||
| 999 | memset(newinfo->counters, 0, countersize); | ||
| 1000 | |||
| 1001 | newinfo->entries = vmalloc(tmp.entries_size); | ||
| 1002 | if (!newinfo->entries) { | ||
| 1003 | ret = -ENOMEM; | ||
| 1004 | goto free_newinfo; | ||
| 1005 | } | ||
| 1006 | if (copy_from_user( | ||
| 1007 | newinfo->entries, tmp.entries, tmp.entries_size) != 0) { | ||
| 1008 | BUGPRINT("Couldn't copy entries from userspace\n"); | ||
| 1009 | ret = -EFAULT; | ||
| 1010 | goto free_entries; | ||
| 1011 | } | ||
| 1012 | 970 | ||
| 1013 | /* the user wants counters back | 971 | /* the user wants counters back |
| 1014 | the check on the size is done later, when we have the lock */ | 972 | the check on the size is done later, when we have the lock */ |
| 1015 | if (tmp.num_counters) { | 973 | if (repl->num_counters) { |
| 1016 | counterstmp = vmalloc(tmp.num_counters * sizeof(*counterstmp)); | 974 | unsigned long size = repl->num_counters * sizeof(*counterstmp); |
| 1017 | if (!counterstmp) { | 975 | counterstmp = vmalloc(size); |
| 1018 | ret = -ENOMEM; | 976 | if (!counterstmp) |
| 1019 | goto free_entries; | 977 | return -ENOMEM; |
| 1020 | } | ||
| 1021 | } | 978 | } |
| 1022 | else | ||
| 1023 | counterstmp = NULL; | ||
| 1024 | 979 | ||
| 1025 | /* this can get initialized by translate_table() */ | ||
| 1026 | newinfo->chainstack = NULL; | 980 | newinfo->chainstack = NULL; |
| 1027 | ret = ebt_verify_pointers(&tmp, newinfo); | 981 | ret = ebt_verify_pointers(repl, newinfo); |
| 1028 | if (ret != 0) | 982 | if (ret != 0) |
| 1029 | goto free_counterstmp; | 983 | goto free_counterstmp; |
| 1030 | 984 | ||
| 1031 | ret = translate_table(net, tmp.name, newinfo); | 985 | ret = translate_table(net, repl->name, newinfo); |
| 1032 | 986 | ||
| 1033 | if (ret != 0) | 987 | if (ret != 0) |
| 1034 | goto free_counterstmp; | 988 | goto free_counterstmp; |
| 1035 | 989 | ||
| 1036 | t = find_table_lock(net, tmp.name, &ret, &ebt_mutex); | 990 | t = find_table_lock(net, repl->name, &ret, &ebt_mutex); |
| 1037 | if (!t) { | 991 | if (!t) { |
| 1038 | ret = -ENOENT; | 992 | ret = -ENOENT; |
| 1039 | goto free_iterate; | 993 | goto free_iterate; |
| 1040 | } | 994 | } |
| 1041 | 995 | ||
| 1042 | /* the table doesn't like it */ | 996 | /* the table doesn't like it */ |
| 1043 | if (t->check && (ret = t->check(newinfo, tmp.valid_hooks))) | 997 | if (t->check && (ret = t->check(newinfo, repl->valid_hooks))) |
| 1044 | goto free_unlock; | 998 | goto free_unlock; |
| 1045 | 999 | ||
| 1046 | if (tmp.num_counters && tmp.num_counters != t->private->nentries) { | 1000 | if (repl->num_counters && repl->num_counters != t->private->nentries) { |
| 1047 | BUGPRINT("Wrong nr. of counters requested\n"); | 1001 | BUGPRINT("Wrong nr. of counters requested\n"); |
| 1048 | ret = -EINVAL; | 1002 | ret = -EINVAL; |
| 1049 | goto free_unlock; | 1003 | goto free_unlock; |
| @@ -1059,7 +1013,7 @@ static int do_replace(struct net *net, const void __user *user, | |||
| 1059 | module_put(t->me); | 1013 | module_put(t->me); |
| 1060 | /* we need an atomic snapshot of the counters */ | 1014 | /* we need an atomic snapshot of the counters */ |
| 1061 | write_lock_bh(&t->lock); | 1015 | write_lock_bh(&t->lock); |
| 1062 | if (tmp.num_counters) | 1016 | if (repl->num_counters) |
| 1063 | get_counters(t->private->counters, counterstmp, | 1017 | get_counters(t->private->counters, counterstmp, |
| 1064 | t->private->nentries); | 1018 | t->private->nentries); |
| 1065 | 1019 | ||
| @@ -1070,10 +1024,9 @@ static int do_replace(struct net *net, const void __user *user, | |||
| 1070 | allocation. Only reason why this is done is because this way the lock | 1024 | allocation. Only reason why this is done is because this way the lock |
| 1071 | is held only once, while this doesn't bring the kernel into a | 1025 | is held only once, while this doesn't bring the kernel into a |
| 1072 | dangerous state. */ | 1026 | dangerous state. */ |
| 1073 | if (tmp.num_counters && | 1027 | if (repl->num_counters && |
| 1074 | copy_to_user(tmp.counters, counterstmp, | 1028 | copy_to_user(repl->counters, counterstmp, |
| 1075 | tmp.num_counters * sizeof(struct ebt_counter))) { | 1029 | repl->num_counters * sizeof(struct ebt_counter))) { |
| 1076 | BUGPRINT("Couldn't copy counters to userspace\n"); | ||
| 1077 | ret = -EFAULT; | 1030 | ret = -EFAULT; |
| 1078 | } | 1031 | } |
| 1079 | else | 1032 | else |
| @@ -1107,6 +1060,59 @@ free_counterstmp: | |||
| 1107 | vfree(newinfo->chainstack[i]); | 1060 | vfree(newinfo->chainstack[i]); |
| 1108 | vfree(newinfo->chainstack); | 1061 | vfree(newinfo->chainstack); |
| 1109 | } | 1062 | } |
| 1063 | return ret; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | /* replace the table */ | ||
| 1067 | static int do_replace(struct net *net, const void __user *user, | ||
| 1068 | unsigned int len) | ||
| 1069 | { | ||
| 1070 | int ret, countersize; | ||
| 1071 | struct ebt_table_info *newinfo; | ||
| 1072 | struct ebt_replace tmp; | ||
| 1073 | |||
| 1074 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | ||
| 1075 | return -EFAULT; | ||
| 1076 | |||
| 1077 | if (len != sizeof(tmp) + tmp.entries_size) { | ||
| 1078 | BUGPRINT("Wrong len argument\n"); | ||
| 1079 | return -EINVAL; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | if (tmp.entries_size == 0) { | ||
| 1083 | BUGPRINT("Entries_size never zero\n"); | ||
| 1084 | return -EINVAL; | ||
| 1085 | } | ||
| 1086 | /* overflow check */ | ||
| 1087 | if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / | ||
| 1088 | NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter)) | ||
| 1089 | return -ENOMEM; | ||
| 1090 | if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) | ||
| 1091 | return -ENOMEM; | ||
| 1092 | |||
| 1093 | countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids; | ||
| 1094 | newinfo = vmalloc(sizeof(*newinfo) + countersize); | ||
| 1095 | if (!newinfo) | ||
| 1096 | return -ENOMEM; | ||
| 1097 | |||
| 1098 | if (countersize) | ||
| 1099 | memset(newinfo->counters, 0, countersize); | ||
| 1100 | |||
| 1101 | newinfo->entries = vmalloc(tmp.entries_size); | ||
| 1102 | if (!newinfo->entries) { | ||
| 1103 | ret = -ENOMEM; | ||
| 1104 | goto free_newinfo; | ||
| 1105 | } | ||
| 1106 | if (copy_from_user( | ||
| 1107 | newinfo->entries, tmp.entries, tmp.entries_size) != 0) { | ||
| 1108 | BUGPRINT("Couldn't copy entries from userspace\n"); | ||
| 1109 | ret = -EFAULT; | ||
| 1110 | goto free_entries; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | ret = do_replace_finish(net, &tmp, newinfo); | ||
| 1114 | if (ret == 0) | ||
| 1115 | return ret; | ||
| 1110 | free_entries: | 1116 | free_entries: |
| 1111 | vfree(newinfo->entries); | 1117 | vfree(newinfo->entries); |
| 1112 | free_newinfo: | 1118 | free_newinfo: |
