git: 76aa36ab4853 - stable/13 - pf: Validate user string nul-termination before copying
Mark Johnston
markj at FreeBSD.org
Wed Aug 11 01:13:44 UTC 2021
The branch stable/13 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=76aa36ab4853eac6e6f5d2617323872a62b80b46
commit 76aa36ab4853eac6e6f5d2617323872a62b80b46
Author: Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-07-28 14:16:42 +0000
Commit: Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-08-11 01:11:41 +0000
pf: Validate user string nul-termination before copying
Some pf ioctl handlers use strlcpy() to copy strings when converting
from user structures to their in-kernel representations. strlcpy()
ensures that the destination will be nul-terminated, but it assumes that
the source is nul-terminated. In particular, it returns the full length
of the source string, so if the source is not nul-terminated, strlcpy()
will keep scanning until it finds a nul byte, and it may encounter an
unmapped page first. Add a helper to validate user strings before
copying.
There are also places where we look up a ruleset using a user-provided
anchor string. In some ioctl handlers we were already nul-terminating
the string, avoiding the same problem, but in other places we were not.
Fix those by nul-terminating as well. Aside from being consistent,
anchors have a maximum length of MAXPATHLEN - 1 so calling strnlen()
might not be so desirable.
Reported by: syzbot+35a1549b4663e9483dd1 at syzkaller.appspotmail.com
Reviewed by: kp
Sponsored by: The FreeBSD Foundation
(cherry picked from commit 64432ad2a2c4b10d3d3411a8ca018e2a35cec97e)
---
sys/netpfil/pf/pf_ioctl.c | 123 ++++++++++++++++++++++++++++++++++------------
1 file changed, 92 insertions(+), 31 deletions(-)
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 6d2403bf213d..edc786d804da 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -283,6 +283,20 @@ extern u_long pf_ioctl_maxcount;
goto target; \
} while (0)
+/*
+ * Copy a user-provided string, returning an error if truncation would occur.
+ * Avoid scanning past "sz" bytes in the source string since there's no
+ * guarantee that it's nul-terminated.
+ */
+static int
+pf_user_strcpy(char *dst, const char *src, size_t sz)
+{
+ if (strnlen(src, sz) == sz)
+ return (EINVAL);
+ (void)strlcpy(dst, src, sz);
+ return (0);
+}
+
static void
pfattach_vnet(void)
{
@@ -1522,14 +1536,17 @@ pf_kpooladdr_to_pooladdr(const struct pf_kpooladdr *kpool,
strlcpy(pool->ifname, kpool->ifname, sizeof(pool->ifname));
}
-static void
+static int
pf_pooladdr_to_kpooladdr(const struct pf_pooladdr *pool,
struct pf_kpooladdr *kpool)
{
+ int ret;
bzero(kpool, sizeof(*kpool));
bcopy(&pool->addr, &kpool->addr, sizeof(kpool->addr));
- strlcpy(kpool->ifname, pool->ifname, sizeof(kpool->ifname));
+ ret = pf_user_strcpy(kpool->ifname, pool->ifname,
+ sizeof(kpool->ifname));
+ return (ret);
}
static void
@@ -1694,15 +1711,30 @@ pf_rule_to_krule(const struct pf_rule *rule, struct pf_krule *krule)
bcopy(&rule->src, &krule->src, sizeof(rule->src));
bcopy(&rule->dst, &krule->dst, sizeof(rule->dst));
- strlcpy(krule->label[0], rule->label, sizeof(rule->label));
- strlcpy(krule->ifname, rule->ifname, sizeof(rule->ifname));
- strlcpy(krule->qname, rule->qname, sizeof(rule->qname));
- strlcpy(krule->pqname, rule->pqname, sizeof(rule->pqname));
- strlcpy(krule->tagname, rule->tagname, sizeof(rule->tagname));
- strlcpy(krule->match_tagname, rule->match_tagname,
+ ret = pf_user_strcpy(krule->label[0], rule->label, sizeof(rule->label));
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(krule->ifname, rule->ifname, sizeof(rule->ifname));
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(krule->qname, rule->qname, sizeof(rule->qname));
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(krule->pqname, rule->pqname, sizeof(rule->pqname));
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(krule->tagname, rule->tagname,
+ sizeof(rule->tagname));
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(krule->match_tagname, rule->match_tagname,
sizeof(rule->match_tagname));
- strlcpy(krule->overload_tblname, rule->overload_tblname,
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(krule->overload_tblname, rule->overload_tblname,
sizeof(rule->overload_tblname));
+ if (ret != 0)
+ return (ret);
ret = pf_pool_to_kpool(&rule->rpool, &krule->rpool);
if (ret != 0)
@@ -1916,6 +1948,8 @@ static int
pf_state_kill_to_kstate_kill(const struct pfioc_state_kill *psk,
struct pf_kstate_kill *kill)
{
+ int ret;
+
bzero(kill, sizeof(*kill));
bcopy(&psk->psk_pfcmp, &kill->psk_pfcmp, sizeof(kill->psk_pfcmp));
@@ -1923,8 +1957,14 @@ pf_state_kill_to_kstate_kill(const struct pfioc_state_kill *psk,
kill->psk_proto = psk->psk_proto;
bcopy(&psk->psk_src, &kill->psk_src, sizeof(kill->psk_src));
bcopy(&psk->psk_dst, &kill->psk_dst, sizeof(kill->psk_dst));
- strlcpy(kill->psk_ifname, psk->psk_ifname, sizeof(kill->psk_ifname));
- strlcpy(kill->psk_label, psk->psk_label, sizeof(kill->psk_label));
+ ret = pf_user_strcpy(kill->psk_ifname, psk->psk_ifname,
+ sizeof(kill->psk_ifname));
+ if (ret != 0)
+ return (ret);
+ ret = pf_user_strcpy(kill->psk_label, psk->psk_label,
+ sizeof(kill->psk_label));
+ if (ret != 0)
+ return (ret);
return (0);
}
@@ -2340,8 +2380,9 @@ DIOCADDRULENV_error:
struct pf_krule *tail;
int rs_num;
- PF_RULES_WLOCK();
pr->anchor[sizeof(pr->anchor) - 1] = 0;
+
+ PF_RULES_WLOCK();
ruleset = pf_find_kruleset(pr->anchor);
if (ruleset == NULL) {
PF_RULES_WUNLOCK();
@@ -2371,8 +2412,9 @@ DIOCADDRULENV_error:
struct pf_krule *rule;
int rs_num;
- PF_RULES_WLOCK();
pr->anchor[sizeof(pr->anchor) - 1] = 0;
+
+ PF_RULES_WLOCK();
ruleset = pf_find_kruleset(pr->anchor);
if (ruleset == NULL) {
PF_RULES_WUNLOCK();
@@ -2561,6 +2603,8 @@ DIOCGETRULENV_error:
u_int32_t nr = 0;
int rs_num;
+ pcr->anchor[sizeof(pcr->anchor) - 1] = 0;
+
if (pcr->action < PF_CHANGE_ADD_HEAD ||
pcr->action > PF_CHANGE_GET_TICKET) {
error = EINVAL;
@@ -3028,7 +3072,7 @@ DIOCGETSTATESV2_full:
break;
}
PF_RULES_WLOCK();
- strlcpy(V_pf_status.ifname, pi->ifname, IFNAMSIZ);
+ error = pf_user_strcpy(V_pf_status.ifname, pi->ifname, IFNAMSIZ);
PF_RULES_WUNLOCK();
break;
}
@@ -3194,19 +3238,23 @@ DIOCGETSTATESV2_full:
struct pf_ifspeed_v1 ps;
struct ifnet *ifp;
- if (psp->ifname[0] != 0) {
- /* Can we completely trust user-land? */
- strlcpy(ps.ifname, psp->ifname, IFNAMSIZ);
- ifp = ifunit(ps.ifname);
- if (ifp != NULL) {
- psp->baudrate32 =
- (u_int32_t)uqmin(ifp->if_baudrate, UINT_MAX);
- if (cmd == DIOCGIFSPEEDV1)
- psp->baudrate = ifp->if_baudrate;
- } else
- error = EINVAL;
- } else
+ if (psp->ifname[0] == '\0') {
+ error = EINVAL;
+ break;
+ }
+
+ error = pf_user_strcpy(ps.ifname, psp->ifname, IFNAMSIZ);
+ if (error != 0)
+ break;
+ ifp = ifunit(ps.ifname);
+ if (ifp != NULL) {
+ psp->baudrate32 =
+ (u_int32_t)uqmin(ifp->if_baudrate, UINT_MAX);
+ if (cmd == DIOCGIFSPEEDV1)
+ psp->baudrate = ifp->if_baudrate;
+ } else {
error = EINVAL;
+ }
break;
}
@@ -3433,7 +3481,9 @@ DIOCGETSTATESV2_full:
break;
}
pa = malloc(sizeof(*pa), M_PFRULE, M_WAITOK);
- pf_pooladdr_to_kpooladdr(&pp->addr, pa);
+ error = pf_pooladdr_to_kpooladdr(&pp->addr, pa);
+ if (error != 0)
+ break;
if (pa->ifname[0])
kif = pf_kkif_create(M_WAITOK);
PF_RULES_WLOCK();
@@ -3469,8 +3519,10 @@ DIOCGETSTATESV2_full:
struct pf_kpool *pool;
struct pf_kpooladdr *pa;
- PF_RULES_RLOCK();
+ pp->anchor[sizeof(pp->anchor) - 1] = 0;
pp->nr = 0;
+
+ PF_RULES_RLOCK();
pool = pf_get_kpool(pp->anchor, pp->ticket, pp->r_action,
pp->r_num, 0, 1, 0);
if (pool == NULL) {
@@ -3490,6 +3542,8 @@ DIOCGETSTATESV2_full:
struct pf_kpooladdr *pa;
u_int32_t nr = 0;
+ pp->anchor[sizeof(pp->anchor) - 1] = 0;
+
PF_RULES_RLOCK();
pool = pf_get_kpool(pp->anchor, pp->ticket, pp->r_action,
pp->r_num, 0, 1, 1);
@@ -3521,6 +3575,8 @@ DIOCGETSTATESV2_full:
struct pf_kruleset *ruleset;
struct pfi_kkif *kif = NULL;
+ pca->anchor[sizeof(pca->anchor) - 1] = 0;
+
if (pca->action < PF_CHANGE_ADD_HEAD ||
pca->action > PF_CHANGE_REMOVE) {
error = EINVAL;
@@ -3652,8 +3708,9 @@ DIOCCHANGEADDR_error:
struct pf_kruleset *ruleset;
struct pf_kanchor *anchor;
- PF_RULES_RLOCK();
pr->path[sizeof(pr->path) - 1] = 0;
+
+ PF_RULES_RLOCK();
if ((ruleset = pf_find_kruleset(pr->path)) == NULL) {
PF_RULES_RUNLOCK();
error = ENOENT;
@@ -3680,8 +3737,9 @@ DIOCCHANGEADDR_error:
struct pf_kanchor *anchor;
u_int32_t nr = 0;
- PF_RULES_RLOCK();
pr->path[sizeof(pr->path) - 1] = 0;
+
+ PF_RULES_RLOCK();
if ((ruleset = pf_find_kruleset(pr->path)) == NULL) {
PF_RULES_RUNLOCK();
error = ENOENT;
@@ -4262,6 +4320,7 @@ DIOCCHANGEADDR_error:
}
PF_RULES_WLOCK();
for (i = 0, ioe = ioes; i < io->size; i++, ioe++) {
+ ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
@@ -4335,6 +4394,7 @@ DIOCCHANGEADDR_error:
}
PF_RULES_WLOCK();
for (i = 0, ioe = ioes; i < io->size; i++, ioe++) {
+ ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
@@ -4411,6 +4471,7 @@ DIOCCHANGEADDR_error:
PF_RULES_WLOCK();
/* First makes sure everything will succeed. */
for (i = 0, ioe = ioes; i < io->size; i++, ioe++) {
+ ioe->anchor[sizeof(ioe->anchor) - 1] = 0;
switch (ioe->rs_num) {
#ifdef ALTQ
case PF_RULESET_ALTQ:
@@ -4477,7 +4538,7 @@ DIOCCHANGEADDR_error:
struct pfr_table table;
bzero(&table, sizeof(table));
- strlcpy(table.pfrt_anchor, ioe->anchor,
+ (void)strlcpy(table.pfrt_anchor, ioe->anchor,
sizeof(table.pfrt_anchor));
if ((error = pfr_ina_commit(&table,
ioe->ticket, NULL, NULL, 0))) {
More information about the dev-commits-src-branches
mailing list