[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>
This commit is contained in:
committed by
David S. Miller
parent
79030ed07d
commit
9fa492cdc1
@@ -333,52 +333,65 @@ int xt_check_match(const struct xt_match *match, unsigned short family,
|
||||
EXPORT_SYMBOL_GPL(xt_check_match);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
int xt_compat_match(void *match, void **dstptr, int *size, int convert)
|
||||
int xt_compat_match_offset(struct xt_match *match)
|
||||
{
|
||||
struct xt_match *m;
|
||||
struct compat_xt_entry_match *pcompat_m;
|
||||
struct xt_entry_match *pm;
|
||||
u_int16_t msize;
|
||||
int off, ret;
|
||||
|
||||
ret = 0;
|
||||
m = ((struct xt_entry_match *)match)->u.kernel.match;
|
||||
off = XT_ALIGN(m->matchsize) - COMPAT_XT_ALIGN(m->matchsize);
|
||||
switch (convert) {
|
||||
case COMPAT_TO_USER:
|
||||
pm = (struct xt_entry_match *)match;
|
||||
msize = pm->u.user.match_size;
|
||||
if (copy_to_user(*dstptr, pm, msize)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
msize -= off;
|
||||
if (put_user(msize, (u_int16_t *)*dstptr))
|
||||
ret = -EFAULT;
|
||||
*size -= off;
|
||||
*dstptr += msize;
|
||||
break;
|
||||
case COMPAT_FROM_USER:
|
||||
pcompat_m = (struct compat_xt_entry_match *)match;
|
||||
pm = (struct xt_entry_match *)*dstptr;
|
||||
msize = pcompat_m->u.user.match_size;
|
||||
memcpy(pm, pcompat_m, msize);
|
||||
msize += off;
|
||||
pm->u.user.match_size = msize;
|
||||
*size += off;
|
||||
*dstptr += msize;
|
||||
break;
|
||||
case COMPAT_CALC_SIZE:
|
||||
*size += off;
|
||||
break;
|
||||
default:
|
||||
ret = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
u_int16_t csize = match->compatsize ? : match->matchsize;
|
||||
return XT_ALIGN(match->matchsize) - COMPAT_XT_ALIGN(csize);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match);
|
||||
#endif
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_offset);
|
||||
|
||||
void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_match *match = m->u.kernel.match;
|
||||
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
|
||||
int pad, off = xt_compat_match_offset(match);
|
||||
u_int16_t msize = cm->u.user.match_size;
|
||||
|
||||
m = *dstptr;
|
||||
memcpy(m, cm, sizeof(*cm));
|
||||
if (match->compat_from_user)
|
||||
match->compat_from_user(m->data, cm->data);
|
||||
else
|
||||
memcpy(m->data, cm->data, msize - sizeof(*cm));
|
||||
pad = XT_ALIGN(match->matchsize) - match->matchsize;
|
||||
if (pad > 0)
|
||||
memset(m->data + match->matchsize, 0, pad);
|
||||
|
||||
msize += off;
|
||||
m->u.user.match_size = msize;
|
||||
|
||||
*size += off;
|
||||
*dstptr += msize;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
|
||||
|
||||
int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_match *match = m->u.kernel.match;
|
||||
struct compat_xt_entry_match __user *cm = *dstptr;
|
||||
int off = xt_compat_match_offset(match);
|
||||
u_int16_t msize = m->u.user.match_size - off;
|
||||
|
||||
if (copy_to_user(cm, m, sizeof(*cm)) ||
|
||||
put_user(msize, &cm->u.user.match_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (match->compat_to_user) {
|
||||
if (match->compat_to_user((void __user *)cm->data, m->data))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_to_user(cm->data, m->data, msize - sizeof(*cm)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*size -= off;
|
||||
*dstptr += msize;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
int xt_check_target(const struct xt_target *target, unsigned short family,
|
||||
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,
|
||||
EXPORT_SYMBOL_GPL(xt_check_target);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
int xt_compat_target(void *target, void **dstptr, int *size, int convert)
|
||||
int xt_compat_target_offset(struct xt_target *target)
|
||||
{
|
||||
struct xt_target *t;
|
||||
struct compat_xt_entry_target *pcompat;
|
||||
struct xt_entry_target *pt;
|
||||
u_int16_t tsize;
|
||||
int off, ret;
|
||||
|
||||
ret = 0;
|
||||
t = ((struct xt_entry_target *)target)->u.kernel.target;
|
||||
off = XT_ALIGN(t->targetsize) - COMPAT_XT_ALIGN(t->targetsize);
|
||||
switch (convert) {
|
||||
case COMPAT_TO_USER:
|
||||
pt = (struct xt_entry_target *)target;
|
||||
tsize = pt->u.user.target_size;
|
||||
if (copy_to_user(*dstptr, pt, tsize)) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
tsize -= off;
|
||||
if (put_user(tsize, (u_int16_t *)*dstptr))
|
||||
ret = -EFAULT;
|
||||
*size -= off;
|
||||
*dstptr += tsize;
|
||||
break;
|
||||
case COMPAT_FROM_USER:
|
||||
pcompat = (struct compat_xt_entry_target *)target;
|
||||
pt = (struct xt_entry_target *)*dstptr;
|
||||
tsize = pcompat->u.user.target_size;
|
||||
memcpy(pt, pcompat, tsize);
|
||||
tsize += off;
|
||||
pt->u.user.target_size = tsize;
|
||||
*size += off;
|
||||
*dstptr += tsize;
|
||||
break;
|
||||
case COMPAT_CALC_SIZE:
|
||||
*size += off;
|
||||
break;
|
||||
default:
|
||||
ret = -ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
u_int16_t csize = target->compatsize ? : target->targetsize;
|
||||
return XT_ALIGN(target->targetsize) - COMPAT_XT_ALIGN(csize);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target);
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target_offset);
|
||||
|
||||
void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_target *target = t->u.kernel.target;
|
||||
struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
|
||||
int pad, off = xt_compat_target_offset(target);
|
||||
u_int16_t tsize = ct->u.user.target_size;
|
||||
|
||||
t = *dstptr;
|
||||
memcpy(t, ct, sizeof(*ct));
|
||||
if (target->compat_from_user)
|
||||
target->compat_from_user(t->data, ct->data);
|
||||
else
|
||||
memcpy(t->data, ct->data, tsize - sizeof(*ct));
|
||||
pad = XT_ALIGN(target->targetsize) - target->targetsize;
|
||||
if (pad > 0)
|
||||
memset(t->data + target->targetsize, 0, pad);
|
||||
|
||||
tsize += off;
|
||||
t->u.user.target_size = tsize;
|
||||
|
||||
*size += off;
|
||||
*dstptr += tsize;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target_from_user);
|
||||
|
||||
int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_target *target = t->u.kernel.target;
|
||||
struct compat_xt_entry_target __user *ct = *dstptr;
|
||||
int off = xt_compat_target_offset(target);
|
||||
u_int16_t tsize = t->u.user.target_size - off;
|
||||
|
||||
if (copy_to_user(ct, t, sizeof(*ct)) ||
|
||||
put_user(tsize, &ct->u.user.target_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (target->compat_to_user) {
|
||||
if (target->compat_to_user((void __user *)ct->data, t->data))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_to_user(ct->data, t->data, tsize - sizeof(*ct)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*size -= off;
|
||||
*dstptr += tsize;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target_to_user);
|
||||
#endif
|
||||
|
||||
struct xt_table_info *xt_alloc_table_info(unsigned int size)
|
||||
|
Reference in New Issue
Block a user