Any way to get VC++ to do scanf type checking?

We had a driver crash today because of some code that was essentially
this:
unsigned char byte;
sscanf (buffer, “%2x”, &byte);

So, I fixed that and yelled at the appropriate person.

But then I wondered why VC++ didn’t error on that. I played around with
the bad code and highest warning levels and everything, and couldn’t get
VC++ to even warn on that code. Jeez! When I was using GCC like 15
years ago, it did scanf / printf type checking, so why wouldn’t VC++
have it now? So, I temporarily gave up on that.

Then I figured that I might as well check our entire code base for
similar bugs. Wow. I’m about 1/3 done after about 2 hours and I’ve
found about 20 more cases of similar naughtiness. I’m not looking
forward to 4 more hours.

So, now I REALLY want to get VC++ to do the work for me, and after a
bunch of searching came up empty.

Does anyone have any ideas or can definitively say “no way”?

(Thanks!)

No way with VC. This kind of checks can be configured in PC-lint for any function; I guess all stdio functions are checked by default. You can also examine if PreFast is able to help (never used it).

P.S. sscanf() in driver? Very bad joke.

Best regards,

Michal Vodicka
UPEK, Inc.
[xxxxx@upek.com, http://www.upek.com]


From: xxxxx@lists.osr.com[SMTP:xxxxx@lists.osr.com] on behalf of Taed Wynnell[SMTP:xxxxx@vertical.com]
Reply To: Windows System Software Devs Interest List
Sent: Saturday, December 17, 2005 4:32 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Any way to get VC++ to do scanf type checking?

We had a driver crash today because of some code that was essentially this:
unsigned char byte;
sscanf (buffer, “%2x”, &byte);

So, I fixed that and yelled at the appropriate person.

But then I wondered why VC++ didn’t error on that. I played around with the bad code and highest warning levels and everything, and couldn’t get VC++ to even warn on that code. Jeez! When I was using GCC like 15 years ago, it did scanf / printf type checking, so why wouldn’t VC++ have it now? So, I temporarily gave up on that.

Then I figured that I might as well check our entire code base for similar bugs. Wow. I’m about 1/3 done after about 2 hours and I’ve found about 20 more cases of similar naughtiness. I’m not looking forward to 4 more hours.

So, now I REALLY want to get VC++ to do the work for me, and after a bunch of searching came up empty.

Does anyone have any ideas or can definitively say “no way”?

(Thanks!)


Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: unknown lmsubst tag argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com

Any way to get VC++ to do scanf type checking?Set up a build environment correctly and they won’t be able to use sscanf. It is not prototyped in any of the ddk include files. If someone adds it to one of your headers, they should be fired and I guess it won’t happen again.
“Taed Wynnell” wrote in message news:xxxxx@ntdev…
We had a driver crash today because of some code that was essentially this:
unsigned char byte;
sscanf (buffer, “%2x”, &byte);

So, I fixed that and yelled at the appropriate person.

But then I wondered why VC++ didn’t error on that. I played around with the bad code and highest warning levels and everything, and couldn’t get VC++ to even warn on that code. Jeez! When I was using GCC like 15 years ago, it did scanf / printf type checking, so why wouldn’t VC++ have it now? So, I temporarily gave up on that.

Then I figured that I might as well check our entire code base for similar bugs. Wow. I’m about 1/3 done after about 2 hours and I’ve found about 20 more cases of similar naughtiness. I’m not looking forward to 4 more hours.

So, now I REALLY want to get VC++ to do the work for me, and after a bunch of searching came up empty.

Does anyone have any ideas or can definitively say “no way”?

(Thanks!)

>years ago, it did scanf / printf type checking, so why wouldn’t VC++

have it now? So, I temporarily gave up on that.

scanf() is obsolete, especially in Windows kernel. The reason is that it is not
safe from buffer overruns at all.

Does anyone have any ideas or can definitively say “no way”?

Go away from scanf(), this is a bad routine.

Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

Any way to get VC++ to do scanf type checking?Curious situation … sscanf
it seems is not export from ntoskrnl … :wink:

“Taed Wynnell” wrote in message news:xxxxx@ntdev…
We had a driver crash today because of some code that was essentially this:
unsigned char byte;
sscanf (buffer, “%2x”, &byte);
So, I fixed that and yelled at the appropriate person.
But then I wondered why VC++ didn’t error on that. I played around with the
bad code and highest warning levels and everything, and couldn’t get VC++ to
even warn on that code. Jeez! When I was using GCC like 15 years ago, it
did scanf / printf type checking, so why wouldn’t VC++ have it now? So, I
temporarily gave up on that.
Then I figured that I might as well check our entire code base for similar
bugs. Wow. I’m about 1/3 done after about 2 hours and I’ve found about 20
more cases of similar naughtiness. I’m not looking forward to 4 more hours.
So, now I REALLY want to get VC++ to do the work for me, and after a bunch
of searching came up empty.
Does anyone have any ideas or can definitively say “no way”?
(Thanks!)

> Any way to get VC++ to do scanf type checking?Curious situation … sscanf

it seems is not export from ntoskrnl … :wink:

Surely, it is a bad and obsolete function, which is supported for compatibility
with the 1970ies obsolete crap.

Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
xxxxx@storagecraft.com
http://www.storagecraft.com

Any way to get VC++ to do scanf type checking?I mean, not that it is curious
that sscanf does not export from ntoskrnl.exe, this is a good thing ™,
but it is therefore curious for sscanf to cause a driver crash?

“Taed Wynnell” wrote in message news:xxxxx@ntdev…
We had a driver crash today because of some code that was essentially this:
unsigned char byte;
sscanf (buffer, “%2x”, &byte);
So, I fixed that and yelled at the appropriate person.
But then I wondered why VC++ didn’t error on that. I played around with the
bad code and highest warning levels and everything, and couldn’t get VC++ to
even warn on that code. Jeez! When I was using GCC like 15 years ago, it
did scanf / printf type checking, so why wouldn’t VC++ have it now? So, I
temporarily gave up on that.
Then I figured that I might as well check our entire code base for similar
bugs. Wow. I’m about 1/3 done after about 2 hours and I’ve found about 20
more cases of similar naughtiness. I’m not looking forward to 4 more hours.
So, now I REALLY want to get VC++ to do the work for me, and after a bunch
of searching came up empty.
Does anyone have any ideas or can definitively say “no way”?
(Thanks!)

> We had a driver crash today because of some code that was

essentially this:
unsigned char byte;
sscanf (buffer, “%2x”, &byte);
So, I fixed that and yelled at the appropriate person.
But then I wondered why VC++ didn’t error on that.

As everyone else has said, you really shouldn’t be using these
functions. Use the strsafe library - it’s consistent, safe, and
generally much nicer to use than the standard C stuff. If you include
<ntstrsafe.h>, you will get warnings about any use of old C string
stuff (3790.1830):

----- Sources:

TARGETNAME=str
TARGETTYPE=DRIVER
TARGETPATH=obj

SOURCES=str.c

------ str.c:

#include <ntddk.h>
#include <ntstrsafe.h>

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING
RegistryPath)
{
char buf[1];
sprintf(buf, “Hello world”);
//RtlStringCbPrintfA(buf, sizeof(buf), “Hello world”);
return STATUS_SUCCESS;
}

-----

C:\dev\str>bcz

1>Compiling - str.c for i386
1>errors in directory c:\dev\str
1>str.c(8) : error C2220: warning treated as error - no object file
generated
1>str.c(8) : error C4995: ‘sprintf’: name was marked as #pragma
deprecated


-----

If the safe string version is used (commented out), it will compile
cleanly and won’t crash. Unfortunately, prefast doesn’t warn about
the bug.

More information:
http://www.microsoft.com/whdc/driver/tips/SafeString.mspx
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
kmarch/hh/kmarch/Other_2fe131c8-834b-4fff-a1c8-3803eeb9324c.xml.asp

Note that you have to follow special instructions from one of the
URLs above if you want to support Win2k with these functions.

-sd

----------------------------------
Steve Dispensa
MVP - Windows DDK
www.kernelmustard.com</ntstrsafe.h></ntddk.h></ntstrsafe.h>

He wants *sscanf* not printf. There’s no strsafe equivalent. In fact, as
far as I can tell, there’s no other even vague equivalent available in
kernel mode at all.

Now, I’m not saying that I think string parsing is something that you
should necessarily do in the kernel rather than user mode (though if
you’re doing some kind of network filtering driver it’s unclear that
doing it in user mode is fast enough)…

But surely using a very well understood function like sscanf *correctly*
is better than implementing your *own* buggy string parsing routines.

If you make sure that *every* field is counted and verify (with a
lint-like app) that the parameters are the correct type, I’m not sure
I’d have a better alternative to suggest if you *really* have to do
string parsing in the kernel.

But really, the kernel isn’t designed for string parsing :-)… it’s a
rather inherently dangerous thing to do.

Of course, that won’t stop people, so maybe it’s time for Microsoft to
implement at least a few safe, if perhaps rudimentary, string parsing
routines in the kernel. RtlUnicodeStringToInteger is nice to have, but
it’s a pretty pathetic way to grunge through ascii.

Steve Dispensa wrote:

> We had a driver crash today because of some code that was essentially
> this:
> unsigned char byte;
> sscanf (buffer, “%2x”, &byte);
> So, I fixed that and yelled at the appropriate person.
> But then I wondered why VC++ didn’t error on that.

As everyone else has said, you really shouldn’t be using these
functions. Use the strsafe library - it’s consistent, safe, and
generally much nicer to use than the standard C stuff. If you include
<ntstrsafe.h>, you will get warnings about any use of old C string stuff
> (3790.1830):
>
> ----- Sources:
>
> TARGETNAME=str
> TARGETTYPE=DRIVER
> TARGETPATH=obj
>
> SOURCES=str.c
>
> ------ str.c:
>
> #include <ntddk.h>
> #include <ntstrsafe.h>
>
> NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING
> RegistryPath)
> {
> char buf[1];
> sprintf(buf, “Hello world”);
> //RtlStringCbPrintfA(buf, sizeof(buf), “Hello world”);
> return STATUS_SUCCESS;
> }
>
> -----
>
> C:\dev\str>bcz
> …
> 1>Compiling - str.c for i386
> 1>errors in directory c:\dev\str
> 1>str.c(8) : error C2220: warning treated as error - no object file
> generated
> 1>str.c(8) : error C4995: ‘sprintf’: name was marked as #pragma deprecated
> …
>
> -----
>
> If the safe string version is used (commented out), it will compile
> cleanly and won’t crash. Unfortunately, prefast doesn’t warn about the bug.
>
> More information:
> http://www.microsoft.com/whdc/driver/tips/SafeString.mspx
>
> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/kmarch/hh/kmarch/Other_2fe131c8-834b-4fff-a1c8-3803eeb9324c.xml.asp
>
>
> Note that you have to follow special instructions from one of the URLs
> above if you want to support Win2k with these functions.
>
> -sd
>
>
> ----------------------------------
> Steve Dispensa
> MVP - Windows DDK
> www.kernelmustard.com
>
>
>


Ray</ntstrsafe.h></ntddk.h></ntstrsafe.h>

Ray Trent wrote:

He wants *sscanf* not printf. There’s no strsafe equivalent. In fact,
as far as I can tell, there’s no other even vague equivalent available
in kernel mode at all.

His specific example was:
sscanf( buffer, “%2x”, &byte );
In that case,
byte = (unsigned char) strtoul( buffer, NULL, 16 );
is a far better solution. It is deterministic, safe, and available in
libcntpr.lib for kernel drivers.

The use of scanf is almost always an indication of either (1)
inexperienced programmers, or (2) throwaway programs.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

strtoul even less safe, though, because there’s no way to specify a
buffer size, unlike as in sscanf( buffer, “%2x”, &byte ), which will
touch at most 2 numeric digits (plus arbitrary whitespace, so it’s not
necessarily safe either unless you scan the whitespace explicitly).

If that weren’t bad enough, in the unlikely event that he *actually*
wanted just 2 characters in the string to be parsed as a hex number
(because, for example, the string is a big pile of hex digits he’s
parsing 1 byte at a time), there’s no way for strtoul to do that.

I’ll admit, though, that using scanf in the kernel is a scary thing that
I’d probably never do, no matter how careful I was, just on general
principles.

Tim Roberts wrote:

Ray Trent wrote:

> He wants *sscanf* not printf. There’s no strsafe equivalent. In fact,
> as far as I can tell, there’s no other even vague equivalent available
> in kernel mode at all.

His specific example was:
sscanf( buffer, “%2x”, &byte );
In that case,
byte = (unsigned char) strtoul( buffer, NULL, 16 );
is a far better solution. It is deterministic, safe, and available in
libcntpr.lib for kernel drivers.

The use of scanf is almost always an indication of either (1)
inexperienced programmers, or (2) throwaway programs.


Ray

Ray Trent wrote:

strtoul even less safe, though, because there’s no way to specify a
buffer size, unlike as in sscanf( buffer, “%2x”, &byte ), which will
touch at most 2 numeric digits (plus arbitrary whitespace, so it’s not
necessarily safe either unless you scan the whitespace explicitly).

Please be careful about your phrasing. Remember that these posts are
stored for eternity, and will be read years from now by newbies and
interpreted as gospel truth uttered by experts.

Saying “strtoul even less safe” than sscanf is simply untrue. sscanf
will happily WRITE beyond what is intended, as was the case in this
particular example. It’s true that strtoul can READ beyond what was
intended, but it will never WRITE to memory. Plus, in the vast majority
of cases, you are passing it a known zero-terminated string from
(usually) the registry. In that case, its enthusiasm is safely bounded.

When faced with a similar task, I usually write my own hex parsing
functions.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Fair enough, though if you’re worried about passing the wrong kind of
pointer to sscanf (I did suggest using lint to find those if you ever
dare to use sscanf), then you should probably be concerned about
*(ULONG*)bytePointer = strtoul(…) as well. Of *course* assigning a
too-long value to a memory location will flork you up. I suspect not
even lint would find that one, especially if the “bytePointer” was
passed in as a void* context :-).

The only difference is that the poor Microsoft compiler doesn’t happen
to catch the bad assignment in the case of sscanf, but does catch the
strtoul misassignment, which naturally is a really good reason never to
use sscanf without something lint-like.

Of course, reading memory can blue screen just as hard if the buffer
happens to reside near a page boundary and you’re at IRQL>=DISPATCH_LEVEL.

Tim Roberts wrote:

Ray Trent wrote:

> strtoul even less safe, though, because there’s no way to specify a
> buffer size, unlike as in sscanf( buffer, “%2x”, &byte ), which will
> touch at most 2 numeric digits (plus arbitrary whitespace, so it’s not
> necessarily safe either unless you scan the whitespace explicitly).

Please be careful about your phrasing. Remember that these posts are
stored for eternity, and will be read years from now by newbies and
interpreted as gospel truth uttered by experts.

Saying “strtoul even less safe” than sscanf is simply untrue. sscanf
will happily WRITE beyond what is intended, as was the case in this
particular example. It’s true that strtoul can READ beyond what was
intended, but it will never WRITE to memory. Plus, in the vast majority
of cases, you are passing it a known zero-terminated string from
(usually) the registry. In that case, its enthusiasm is safely bounded.

When faced with a similar task, I usually write my own hex parsing
functions.


Ray