From: Phil Sutter <phil@nwl.cc> Date: 2017-08-17 17:12:08
This series collects patches from v1 dealing with code potentially
leaving string buffers unterminated. This does not include situations
where it happens for parsed interface names since an overall solution
was attempted for that and it's state is still unclear due to lack of
feedback from upstream.
No changes to the actual patches, just splitting into smaller series.
Phil Sutter (7):
ipntable: Make sure filter.name is NULL-terminated
xfrm_state: Make sure alg_name is NULL-terminated
lib/fs: Fix format string in find_fs_mount()
lib/inet_proto: Make sure destination buffers are NULL-terminated
lnstat_util: Simplify alloc_and_open() a bit
tc/m_xt: Fix for potential string buffer overflows
lib/ll_map: Make sure im->name is NULL-terminated
ip/ipntable.c | 3 ++-
ip/xfrm_state.c | 3 ++-
lib/fs.c | 2 +-
lib/inet_proto.c | 9 ++++++---
lib/ll_map.c | 4 ++--
misc/lnstat_util.c | 7 ++-----
tc/m_xt.c | 7 ++++---
7 files changed, 19 insertions(+), 16 deletions(-)
--
2.13.1
From: Phil Sutter <phil@nwl.cc> Date: 2017-08-17 17:10:14
- Use strncpy() when writing to target->t->u.user.name and make sure the
final byte remains untouched (xtables_calloc() set it to zero).
- 'tname' length sanitization was completely wrong: If it's length
exceeded the 16 bytes available in 'k', passing a length value of 16
to strncpy() would overwrite the previously NULL'ed 'k[15]'. Also, the
sanitization has to happen if 'tname' is exactly 16 bytes long as
well.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
tc/m_xt.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
@@ -277,8 +278,8 @@ static int parse_ipt(struct action_util *a, int *argc_p,}fprintf(stdout," index %d\n",index);-if(strlen(tname)>16){-size=16;+if(strlen(tname)>=16){+size=15;k[15]=0;}else{size=1+strlen(tname);
From: Phil Sutter <phil@nwl.cc> Date: 2017-08-17 17:11:36
A field width of 4096 allows fscanf() to store that amount of characters
into the given buffer, though that doesn't include the terminating NULL
byte. Decrease the value by one to leave space for it.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
lib/fs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Phil Sutter <phil@nwl.cc> Date: 2017-08-17 17:13:54
Relying upon callers and using unsafe strcpy() is probably not the best
idea. Aside from that, using snprintf() allows to format the string for
lf->path in one go.
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
misc/lnstat_util.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
@@ -125,7 +125,8 @@ static int xfrm_algo_parse(struct xfrm_algo *alg, enum xfrm_attr_type_t type,fprintf(stderr,"warning: ALGO-NAME/ALGO-KEYMAT values will be sent to the kernel promiscuously! (verifying them isn't implemented yet)\n");#endif-strncpy(alg->alg_name,name,sizeof(alg->alg_name));+strncpy(alg->alg_name,name,sizeof(alg->alg_name)-1);+alg->alg_name[sizeof(alg->alg_name)-1]='\0';if(slen>2&&strncmp(key,"0x",2)==0){/* split two chars "0x" from the top */
@@ -633,7 +633,8 @@ static int ipntable_show(int argc, char **argv)}elseif(strcmp(*argv,"name")==0){NEXT_ARG();-strncpy(filter.name,*argv,sizeof(filter.name));+strncpy(filter.name,*argv,sizeof(filter.name)-1);+filter.name[sizeof(filter.name)-1]='\0';
Why not check for overflow instead?
if (filter.name[sizeof(filter.name) - 1])
usage("filer name too long");
sizeof(filter.name) is 1024, which is maybe a bit over the top for
something a user would input. So I found a better way avoiding all this
at once: I made filter.name a const char *, then just assigned *argv to
it. This should be safe since rtnl_dump_filter() and therefore
print_ntable() callback is called from inside ipntable_show() so *argv
is not accessed outside of it's scope.
What do you think?
Thanks, Phil
@@ -633,7 +633,8 @@ static int ipntable_show(int argc, char **argv)}elseif(strcmp(*argv,"name")==0){NEXT_ARG();-strncpy(filter.name,*argv,sizeof(filter.name));+strncpy(filter.name,*argv,sizeof(filter.name)-1);+filter.name[sizeof(filter.name)-1]='\0';
Why not check for overflow instead?
if (filter.name[sizeof(filter.name) - 1])
usage("filer name too long");
sizeof(filter.name) is 1024, which is maybe a bit over the top for
something a user would input. So I found a better way avoiding all this
at once: I made filter.name a const char *, then just assigned *argv to
it. This should be safe since rtnl_dump_filter() and therefore
print_ntable() callback is called from inside ipntable_show() so *argv
is not accessed outside of it's scope.
What do you think?
There isn't a scope problem, *argv is program data (written to unusable
stack space by the kernel during exec.
If the filter is done in userpace it is ok, but I'd have thought
it would be passed to kernel later on?
David
@@ -120,11 +120,11 @@ int ll_remember_index(const struct sockaddr_nl *who,return0;}-im=malloc(sizeof(*im));+im=calloc(1,sizeof(*im));if(im==NULL)return0;im->index=ifi->ifi_index;-strcpy(im->name,ifname);+strncpy(im->name,ifname,IFNAMSIZ-1);im->type=ifi->ifi_type;im->flags=ifi->ifi_flags;
This is not really necessary. kernel won't return
an ifname with a length >= IFNAMSIZ.
If you wanted to future proof it, why not use variable size allocation
@@ -633,7 +633,8 @@ static int ipntable_show(int argc, char **argv)}elseif(strcmp(*argv,"name")==0){NEXT_ARG();-strncpy(filter.name,*argv,sizeof(filter.name));+strncpy(filter.name,*argv,sizeof(filter.name)-1);+filter.name[sizeof(filter.name)-1]='\0';
Why not check for overflow instead?
if (filter.name[sizeof(filter.name) - 1])
usage("filer name too long");
sizeof(filter.name) is 1024, which is maybe a bit over the top for
something a user would input. So I found a better way avoiding all this
at once: I made filter.name a const char *, then just assigned *argv to
it. This should be safe since rtnl_dump_filter() and therefore
print_ntable() callback is called from inside ipntable_show() so *argv
is not accessed outside of it's scope.
What do you think?
There isn't a scope problem, *argv is program data (written to unusable
stack space by the kernel during exec.
Ah, thanks for the info!
If the filter is done in userpace it is ok, but I'd have thought
it would be passed to kernel later on?
No, it just calls sends RTM_GETNEIGHTBL to kernel and throws away
anything that doesn't match the filter. So filtering happens in
user space exclusively.
Thanks, Phil
@@ -35,8 +35,10 @@ const char *inet_proto_n2a(int proto, char *buf, int len)pe=getprotobynumber(proto);if(pe){icache=proto;-strncpy(ncache,pe->p_name,16);-strncpy(buf,pe->p_name,len);+strncpy(ncache,pe->p_name,15);+ncache[15]='\0';+strncpy(buf,pe->p_name,len-1);+buf[len]='\0';returnbuf;}snprintf(buf,len,"ipproto-%d",proto);
@@ -62,7 +64,8 @@ int inet_proto_a2n(const char *buf)pe=getprotobyname(buf);if(pe){icache=pe->p_proto;-strncpy(ncache,pe->p_name,16);+strncpy(ncache,pe->p_name,15);+ncache[15]='\0';returnpe->p_proto;}return-1;
Depending on proto name to be 15 characters or less is a silly
choice. Why not use strdup() and do it right?
Yes, I was puzzled by that as well. Luckily the longest proto name in my
/etc/protocols is rsvp-e2e-ignore which is 15 chars long. I'll change
the patch to use dynamic allocation.
Thanks, Phil