RE: how to ensure atomic code in a kernel mode driver

>

>(3) you can replace p->thisandthat
> by a simple thisandthat,

Which is obfuscating, and thus bad in terms of securiry.

This is the same group of gentlemen that think that calling the hardware as
knowledgeable
practitioners is bad but calling a Microsoft API written by interns like my
then 16 year old
daughter is not risky nor obfuscating given the incorrect or lacking
documentation.

C++ is not as great as the hype would present it, but it helps me reduce
the debugging time/
designing+coding time ratio from 2:1 to about 0.75:1. Your mileage will vary.

Stop attacking a very important tool. It’s just a tool. If you like to put
nails with your wrench feel
free to do it. Just don’t use my wrench. All my drivers were written in C++
and they are as
reliable, efficient and predictable as any driver can be.

George Blat
NT with real time performance
8016 188th SW, Edmonds, WA 98026

phone: 425-775-7475
fax: 781-998-5940
mailto:xxxxx@ntrealtime.com

>and (4) you can make those fields private so that
> they’ll be opaque to the caller no matter what, the compiler won’t even
> allow the caller to reference that field.

But a stray pointer will be able to do this anyway.

> in your call, you no longer have to use things such as p->thisandthat
inside
> your code,

This decreases the code readability and clearness, and thus introduces a
potential for hard-to-find bugs.

Don’t you know that nearly always the fields in C++ are named started from
“m_”, to avoid this particular mess?

> packet *p = new packet(…);
>
> and your memory allocation is automatic and needs no size management;

Now is it better then malloc()?

> on your own either way, but in C++ at least you can use references instead
> of pointers. Reference counts are managed inside the object:

Not so. C++ references like int& do not apply any counting.

> handles them for you. And indeed, C++ can’t stop you from casting pointers,
> but you don’t need to use pointers: you can use references instead, and
> goodbye casting.

In any case, there is a need of using the arbitrary-sized buffers of
bytes, for
IO, for instance. C++ will not help with them.

> runtime. Anything that needs SMP synchronization could go through an IL
> monitor construct, which gets compiled into spinlocks,

And why explicit spinlock calls are worse?

> at runtime ! No more synchronization or memory management of any kind it’s
> all automatic or buried inside the OO model.

Any “buried inside” things are bad. All must be clear and at surface, to
facilitate bugfixing.

> to write the high level logic, and any junior programmer could write a
> driver !

…and will be halted in his work by the very first BSOD at strange call
stack.

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


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

You are currently subscribed to ntdev as: xxxxx@ntrealtime.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

In many areas simple programs are called macros. If you don’t define
the context within you are talking you can say anything that comes to
your mind. In the context of C++ (and C) macros are well “defined” and what
you’re claiming doesn’t do anything but obfuscation. You can define
anything that you don’t like as syntactic sugar, or saccharine or what
have you. I here define your use of English as syntactic sugar over
using your hands and grunts to try to communicate.

George Blat

At 10:33 PM 12/5/2003, you wrote:

The “new” keyword is a macro. Not in the sense of a #define. It’s a
macro in the sense that it is just syntactic sugar for a call to the
global “void * operator new (size_t len)” function, and then to a call
to a constructor of the class. This is a macro in the conceptual sense,
not a #define. Duh.

Nobody can access amunge? Bullshit. All I have to do is copy your
header, change “private:” to “public:”, and I’m done. That, or I can
generate a method signature that matches it and calls it.

You still don’t get it. I’m talking about REAL type-safety, as in CLR.

– arlie

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Moreira, Alberto
Sent: Friday, December 05, 2003 5:38 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver

I’ll only reply to the technical points. The sequence

CFoo * instance = (CFoo *) 0x12345678;
instance->method(); // boom

is C disguised as C++, and you get what you shop for. If you’re doing it
in
C++, you can use references instead of pointers: no cast, and no boom.
Indeed, no language can prevent you from acting like a jackass, but some
languages provide you with alternatives that make it less likely you
will.

The “this” pointer is, indeed, a hidden pointer, but obviously, you’re
passing the worry of maintaining it to the compiler where it belongs.
Heck, you are willing to delegate to the API at runtime, which is
inherently unsafe (even the runtime stack size cannot be guaranteed to
accomodate you
!) and yet you grumble when you’re allowed to delegate to the compiler,
which is eons safer ? One ounce of compile-time checking is worth a ton
of runtime shit. And that’s additional protection too, because if I
issue “datafield” without a pointer or a class:: in front of it, what do
you know, it’s an implied “this”: again, one more chore delegated to the
compiler. And that helps preventing us from acting like jackasses.

The “new” keyword is not a macro - it’s an OPERATOR. It can be
overridden through the standard C++ mechanisms.

As for “same level of protection”, try this. Assume I have the following
program:

class b
{
private:
class a
{
public: int amunge(…);
};
a aobj;
public:
NTSTATUS bmunge(…)
{
aobj.amunge(…);
}
};

Nobody outside class b can access amunge(). Now, write me a C program
that doesn’t let anyone but objects of class b to invoke function
amunge. Once you manage it, try this one:

class a
{
protected:
int amunge() { … }
};
class b: private a
{
public:
int bmunge() { return amunge(); }
};

and again, write me a C program that doesn’t let anyone but objects of
class b, or derived from class a, to invoke function amunge. In this
last case, even the sequence

main()
{
a aobj;
aobj.amunge();

}

will be frigged by the compiler as a violation; if I want that to work,
I must open my protection and declare amunge as public instead of
protected.
Bottomline: I have a three-level C++ function call hierarchy here, duly
scoped and duly encapsulated, while C only gives me two. I could have
multiple levels if I wanted too; not in C.

Alberto.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com]On Behalf Of Arlie Davis
Sent: Friday, December 05, 2003 4:48 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver

Bullshit. C++ provides the exact same level of protection as C –
virtually none. I’m not throwing a tantrum, I’m insulted that you think
I don’t understand C or C++, when I’ve been earning my living with them
(and assembly, and years of k-mode development, blah blah blah) for
many, many years.

I never said that C++ doesn’t provide static type checking. It does –
but it does NOT prevent you from violating its own type safety. It
still provides ABSOLUTELY NO constraints to what developers can do. And
most large-scale applications REQUIRE you to violate type safety to get
any real work done. C++ is just syntactic sugar. In case you didn’t
know, the first C++ compilers were just front-ends – they just
transformed the C++ code into equivalent C code.

“this” management is hardly revoluationary, Alberto. It’s JUST A
POINTER. Just because you don’t see it in the argument list, doesn’t
mean it isn’t there. It isn’t any more protected than any other
pointer. It’s just the “favorite” pointer of the compiler, within a
given class.

The “new” keyword in C++ is just a macro over “malloc” (or whatever
malloc-equivalent you want to use). There is nothing sacred about it.
Again, STOP LECTURING – I’ve been doing this for DECADES and yes, I
understand ALL OF THIS. You aren’t contributing anything new. And I
can still construct an “object” by doing this:

CFoo * instance = (CFoo *) 0x12345678;
instance->method(); // boom

I’m not going to continue this bullshit argument any more. C/C++ are
fundamentally flawed. Don’t bother telling me all about the joys of OOP
– I know all about it, I’ve been doing all of this for YEARS, and not
just as some leaf-node. I’ve managed teams of developers, on projects
that took years to develop and release. Apparently you don’t get it
that *I* get it, so this argument is just totally pointless.

– arlie

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Moreira, Alberto
Sent: Friday, December 05, 2003 4:22 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: how to ensure atomic code in a kernel mode driver

Arlie, you stated that C++ doesn’t protect. I just gave you a fair
amount of examples of how it does protect. But if you’re as senior as
you claim, you will know well enough the difference in level between C
and C++. If you don’t, so be it, but at least give me a few valid
technical points as to why, tantrums won’t do - remember, I have pointed
hair.

C++ gives you additional type checking and safety because member
C++ functions
are now part of the class. For example, if you have a function

NTSTATUS blahblah (packet *p, int packetLength, …) { . // references
to
p->thisandthat; … }

your have near zero protection for both the pointer and the length. If
now you say

NTSTATUS packet::blahblah(…references to thisandthat, no p-> needed in
this context ! …) { }

You’re more protected, because (1) no more direct references to pointer
p in your calls, actually, no need for that pointer at all, (2) no more
need for the programmer to handle packet lengths, (3) you can replace
p->thisandthat by a simple thisandthat, and (4) you can make those
fields private so that they’ll be opaque to the caller no matter what,
the compiler won’t even allow the caller to reference that field. You
also have two less parameters in your call, you no longer have to use
things such as p->thisandthat inside your code, because you KNOW that
“thisandthat” are fields inside the object: a shitload of pointer
dereferencing has been avoided and that’s protection; and if you use
references instead of pointers, you don’t even run into casting issues.
As for memory abuse, try

packet *p = new packet(…);

and your memory allocation is automatic and needs no size management;
delete your memory when you delete your object, presto, gone. Deleting a
deleted object can only happen if you’re using pointers, but then, good
luck: you’re on your own either way, but in C++ at least you can use
references instead of pointers. Reference counts are managed inside the
object: it’s easy enough to extend the class with an interface that
manages the counter automatically for you, so that the caller doesn’t
know about it; and circularity is guaranteed not to happen because you
KNOW that the counters are incremented every way in and decremented
every way out, because that’s the way your object works ! In garbage
collected languages such as C# it’s even better: you don’t need to worry
about those counters, the compiler handles them for you. And indeed, C++
can’t stop you from casting pointers, but you don’t need to use
pointers: you can use references instead, and goodbye casting. Also,
precisely because there’s nothing in the DDK that prevents you from
doing harm, it’s a good idea to encapsulate those things with classes
that guarantee some sanity - provided of course you use them. Believe
me, the highest level language won’t prevent you from acting like a
jackass, but at least they’ll give you plenty of ways to choose that are
as far from stupidity as it can gets.

Until we have a kernel side CLR - and I’m all for it, let’s hide all
that shit inside a C# managed code model and get the JIT compiler to
generate compiled code at DriverEntry time - we have to live with the
nonsense we have, and I find C++ well superior to C in that respect. And
hey, wouldn’t it be great ? The JIT Compiler could know whether you’re
running on a single processor or on a multiprocessor, or on a Xeon, or
on an Opteron, and compile your code differently according to what it
finds out about the runtime. Anything that needs SMP synchronization
could go through an IL monitor construct, which gets compiled into
spinlocks, barriers or what else at runtime ! No more synchronization or
memory management of any kind it’s all automatic or buried inside the OO
model. You could write a driver in C#, debug it inside MSVC.NET in the
user side, then just move the IL to kernel side, done: all low level
stuff buried inside the IL, all you have to do is to write the high
level logic, and any junior programmer could write a driver !

Alberto.


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

You are currently subscribed to ntdev as: xxxxx@compuware.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

The contents of this e-mail are intended for the named addressee only.
It
contains information that may be confidential. Unless you are the named
addressee or an authorized designee, you may not copy or use it, or
disclose
it to anyone else. If you received it in error please notify us
immediately
and then destroy it.


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

You are currently subscribed to ntdev as: xxxxx@sublinear.org
To unsubscribe send a blank email to xxxxx@lists.osr.com


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

You are currently subscribed to ntdev as: xxxxx@ntrealtime.com
To unsubscribe send a blank email to xxxxx@lists.osr.com