Discussion:
[9fans] lpdaemon
(too old to reply)
yaroslav
2013-06-05 11:38:17 UTC
Permalink
in /sys/src/cmd/lp/lpdaemon.c:297,310

These
info.host[strlen(info.host)] = '\0';

info.user[strlen(info.user)] = '\0';

look nonsence as zeros are placed exactly where they already are.
Should read as in following instead:

info.host[NAMELEN] = '\0';

info.user[NAMELEN] = '\0';

shoudn't it?
erik quanstrom
2013-06-05 13:06:36 UTC
Permalink
i agree ... applied to 9atom.

Subject: [sources] applied patch: /n/atom/patch/applied/lpdaemonnit
Reply-To: ***@9atom.org

email
***@quanstro.net
readme
Subject: [9fans] lpdaemon
in /sys/src/cmd/lp/lpdaemon.c:297,310

These
info.host[strlen(info.host)] = '\0';

info.user[strlen(info.user)] = '\0';

look nonsence as zeros are placed exactly where they already are.
Should read as in following instead:

info.host[NAMELEN] = '\0';

info.user[NAMELEN] = '\0';
removed

files
/sys/src/cmd/lp/lpdaemon.c lpdaemon.c

/sys/src/cmd/lp/lpdaemon.c lpdaemon.c
lpdaemon.c.orig:299,305 - lpdaemon.c:299,305
strncpy(info.host, "unknown", NAMELEN);
else
strncpy(info.host, (const char *)&ap[1], NAMELEN);
- info.host[strlen(info.host)] = '\0';
+ info.host[NAMELEN] = '\0';
break;
case 'P':
if (ap[1] == '\0')
lpdaemon.c.orig:306,312 - lpdaemon.c:306,312
strncpy(info.user, "unknown", NAMELEN);
else
strncpy(info.user, (const char *)&ap[1], NAMELEN);
- info.user[strlen(info.user)] = '\0';
+ info.user[NAMELEN] = '\0';
break;
}
}
------
merge...backup...copy...
cpfile lpdaemon.c /n/dist/sys/src/cmd/lp/lpdaemon.c
# remove these files if you want. I will not remove them for you
# ($patch/undo will not restore them)
done
erik quanstrom
2013-06-05 13:20:04 UTC
Permalink
The first opportunity to write a nil byte should always be taken.
Using sizeof only means that in corner cases memory disclosure may
occur between where the nil should be and the end of the array. While
this isn't a security critical app, it is still good coding practice.
x = strlen(info.host) < sizeof info.host ? strlen() : sizeof ;
info.host[x] = 0;
let's start at the beginning. strncpy is not good coding practice.
and lpdaemon is not well written by today's standards. ☺

however, unless i'm missing something, the code has exactly that.

/sys/src/cmd/lp/lpdaemon.c:297,310
case 'H':
if (ap[1] == '\0')
strncpy(info.host, "unknown", NAMELEN);
else
strncpy(info.host, (const char *)&ap[1], NAMELEN);
info.host[NAMELEN] = '\0';
break;
case 'P':
if (ap[1] == '\0')
strncpy(info.user, "unknown", NAMELEN);
else
strncpy(info.user, (const char *)&ap[1], NAMELEN);
info.user[NAMELEN] = '\0';
break;

- erik
Don Bailey
2013-06-05 13:40:02 UTC
Permalink
Not exactly. But, functionally close enough.

I skipped commenting on strncpy to ignore the plethora of issues with lpd and focus on the question at hand.

D
Post by erik quanstrom
The first opportunity to write a nil byte should always be taken.
Using sizeof only means that in corner cases memory disclosure may
occur between where the nil should be and the end of the array. While
this isn't a security critical app, it is still good coding practice.
x = strlen(info.host) < sizeof info.host ? strlen() : sizeof ;
info.host[x] = 0;
let's start at the beginning. strncpy is not good coding practice.
and lpdaemon is not well written by today's standards. ☺
however, unless i'm missing something, the code has exactly that.
/sys/src/cmd/lp/lpdaemon.c:297,310
if (ap[1] == '\0')
strncpy(info.host, "unknown", NAMELEN);
else
strncpy(info.host, (const char *)&ap[1], NAMELEN);
info.host[NAMELEN] = '\0';
break;
if (ap[1] == '\0')
strncpy(info.user, "unknown", NAMELEN);
else
strncpy(info.user, (const char *)&ap[1], NAMELEN);
info.user[NAMELEN] = '\0';
break;
- erik
Don Bailey
2013-06-05 13:13:25 UTC
Permalink
The first opportunity to write a nil byte should always be taken. Using sizeof only means that in corner cases memory disclosure may occur between where the nil should be and the end of the array. While this isn't a security critical app, it is still good coding practice.

x = strlen(info.host) < sizeof info.host ? strlen() : sizeof ;
info.host[x] = 0;

D
Post by yaroslav
in /sys/src/cmd/lp/lpdaemon.c:297,310
These
info.host[strlen(info.host)] = '\0';

info.user[strlen(info.user)] = '\0';
look nonsence as zeros are placed exactly where they already are.
info.host[NAMELEN] = '\0';

info.user[NAMELEN] = '\0';
shoudn't it?
Friedrich Psiorz
2013-06-05 13:38:50 UTC
Permalink
I think your code is wrong. If the NUL byte is present, it doesn't do
anything, however if it is not there, strlen will read more than it
should, and possibly try to read some invalid address.
In case info.host is a fixe size array, a simple
info.host[sizeof info.host - 1] = 0;
would do.
Post by Don Bailey
The first opportunity to write a nil byte should always be taken. Using sizeof only means that in corner cases memory disclosure may occur between where the nil should be and the end of the array. While this isn't a security critical app, it is still good coding practice.
x = strlen(info.host) < sizeof info.host ? strlen() : sizeof ;
info.host[x] = 0;
D
Don Bailey
2013-06-05 13:54:50 UTC
Permalink
You're absolutely correct if the length of value to be copied is not validated prior to the copy. Then, an invalid page could be hit if no nil is present within the array or beyond.

I wasn't providing a verbatim patch (notice the function and operator weren't filled in). I was just providing the sequence of events that should occur. Eric points out correctly that strncpy effectively performs the first operation on the user's behalf. The second is achieved through the write to N.

To be verbose, my bypassing of strncpy is due to issues I've encountered in multi-threaded code. e.g. Don't trust libc copy functions in MT envs, always check post call.

An interesting and sometimes desirable effect of crashing on an invalid page read is that if memory can be corrupted, a consistent unexploitable crash is better than entering a context where the bug becomes exploitable.

D
Post by Friedrich Psiorz
I think your code is wrong. If the NUL byte is present, it doesn't do
anything, however if it is not there, strlen will read more than it
should, and possibly try to read some invalid address.
In case info.host is a fixe size array, a simple
info.host[sizeof info.host - 1] = 0;
would do.
Post by Don Bailey
The first opportunity to write a nil byte should always be taken. Using sizeof only means that in corner cases memory disclosure may occur between where the nil should be and the end of the array. While this isn't a security critical app, it is still good coding practice.
x = strlen(info.host) < sizeof info.host ? strlen() : sizeof ;
info.host[x] = 0;
D
erik quanstrom
2013-06-05 14:09:27 UTC
Permalink
Post by Don Bailey
You're absolutely correct if the length of value to be copied is not
validated prior to the copy. Then, an invalid page could be hit if no
nil is present within the array or beyond.
wrong. strncpy only copies up to the specified maximum.
the code is ugly but correct.
Post by Don Bailey
To be verbose, my bypassing of strncpy is due to issues I've
encountered in multi-threaded code. e.g. Don't trust libc copy
functions in MT envs, always check post call.
this sounds like your saying that because you had trouble in
a multithreaded unix application, then without examining the
code at hand, it is pronounced to have the same issue.

that sounds like equivocation to me. the code is correct.
and in all cases nul-terminated, and any unused bytes are
0.

i only object to strncpy because it requires extra work. seprint,
snprint are a bit heavy weight but tend to produce cleaner looking
code. ymmv.

- erik
Don Bailey
2013-06-05 14:29:25 UTC
Permalink
You get that I'm talking about the subsequent read back after copy, right? No need to be so competitive :)

Also, you're making strange presumptions about me having presumptions. I'm not trying to say you're wrong or a poor coder, Erik. I was simply offering my point of view.

Before this thread dissolves into typical Plan 9 waste I'll admit that my first mistake was to make the poor decision to reply to a 9fans email.

D
Post by erik quanstrom
Post by Don Bailey
You're absolutely correct if the length of value to be copied is not
validated prior to the copy. Then, an invalid page could be hit if no
nil is present within the array or beyond.
wrong. strncpy only copies up to the specified maximum.
the code is ugly but correct.
Post by Don Bailey
To be verbose, my bypassing of strncpy is due to issues I've
encountered in multi-threaded code. e.g. Don't trust libc copy
functions in MT envs, always check post call.
this sounds like your saying that because you had trouble in
a multithreaded unix application, then without examining the
code at hand, it is pronounced to have the same issue.
that sounds like equivocation to me. the code is correct.
and in all cases nul-terminated, and any unused bytes are
0.
i only object to strncpy because it requires extra work. seprint,
snprint are a bit heavy weight but tend to produce cleaner looking
code. ymmv.
- erik
Loading...