Tech Support > Microsoft Windows > Drivers > OS Question
OS Question
Posted by Hugo gleaves@hotmail.com> on May 11th, 2008


Hi
This isnt strictly about a driver issue, but many driver coders may have
insights.

Basically we plan to user some of the Interlock functions (exchange,
exchange compare etc) as the basis for a simple fast lock (not in driver code
though).

However, we are also placing the integer values in pages that have had their
access set to Read Only. Currently when code attempts to write to such a page
we trap the exception, run a few checks and then update the page to have
Write access.

This is all standard stuff, BUT if we locate an integer in such a page AND
try to use an interlock exchnage kind of function on it, it raises an
interesting question.

The interlock operations are defined as being uniterruptible BUT if they are
in a ReadOnly page the operation WILL be interrupted (by the exception that
prevents writes to read only pages).

So would it work or not??

Any insights much appreciated.

PS: We are using XP, Vista, Server 2003 and 2008 both 32-bit and 64-bit.

Posted by Pavel A. on May 11th, 2008


"Hugogleaves@hotmail.com>" <hugh<underbar> wrote in message
news:2A136AC4-76AD-4404-B0C1-67E6EC6BC15E@microsoft.com...
Ok I'll try to answer this, as there's still no other replies.

Interlocked... functions do work with variables in pageable memory.
If these can be interrupted by paging operations, they can be
interrupted by other resumable exceptions as well;
so it will work exactly in same way.

Regards,
--PA



Posted by David Craig on May 11th, 2008


Here are the comments from InterlockedExchange which can be used to do
almost any interlocked operation.

InterlockedExchange should be used instead of ExInterlockedExchangeUlong,
because it is both faster and more efficient.

InterlockedExchange is implemented inline by the compiler when appropriate
and possible. It does not require a spin lock and can therefore be safely
used on pageable data.

A call to InterlockedExchange routine is atomic only with respect to other
InterlockedXxx calls.

Callers of InterlockedExchange can be running at any IRQL.

End comments.

If the data types are simple such as 32 or 64 bits depending upon the OS
target, this is a simple solution. The data item will reside within one
page unless you attempt some weird packing in structures. Creating the
source with code listing from the compiler can allow you to verify that one
instruction is used to update the data item. The interlocked operation will
be uninterruptable since the entire data item is updated at once. The fact
that the data is located in a read only page has no impact on the
uninterruptible-ness of the instruction as the instruction will not being
until the data can be written. Yes, another processor could come along
after the page has been changed to writable and it would get the operation
to complete before the thread that started the sequence can actually get
back to trying the interlocked operation. I would think long and hard about
just making pages read only when they must be written to with any frequency.

"Hugo gleaves@hotmail.com>" <hugh<underbar> wrote in message
news:2A136AC4-76AD-4404-B0C1-67E6EC6BC15E@microsoft.com...


Posted by Alexander Grigoriev on May 11th, 2008


An operation that involves memory modification will only start if the
destination is writeable, no matter if it's interlocked or not. An operation
won't be interrupted in between and then resumed.

I'm not sure why you want to modify page protection on the fly. If this is
for debugging, it's OK. If you base your design on this, it won't work
reliably.

"Hugo gleaves@hotmail.com>" <hugh<underbar> wrote in message
news:2A136AC4-76AD-4404-B0C1-67E6EC6BC15E@microsoft.com...


Posted by Hugo gleaves@hotmail.com> on May 11th, 2008




"David Craig" wrote:

It seems from what you both say, that this will work in a predictable and
intuitive way; of course I will test the code heavily but I agree that it
should operate "as expected" under these circumstances.

In response to David's point, we do this to meet a design objective. We must
prevent errant user code from writing to pages that (may) contain system
structures. When a "user" calls one of our API functions all pages (in the
managed area) are ReadOnly (so that any errant code cannot trash system data)
but when API code attempts to update system data an exception is raised.

The exception handler runs and examines the details about what kind of data
was being written to and why, if this is determined to be valid we make the
page ReadWrite and record this fact.

Because API calls may be nested arbitrarily, we have logic to track the
"depth" of such calls, when the "outermost" API exits, this to ois detected
and the page (all impacted pages in fact) are reset to ReadOnly so that any
attempt by user (calling) code to write to such pages is trapped.

This works and means that NO erroneous code can ever inadvertently damage
system structures in shared memory.

Because our custom read/write spinlock code refers to fields in system
structures that are (therefore) in ReadOnly pages, we have do deal with this
question.

Anyway...I will now code up some formal test code and stress this out.

Thanks again to both of you for your comments.

Hugh


Posted by Hugo gleaves@hotmail.com> on May 11th, 2008




"Alexander Grigoriev" wrote:


Posted by Hugo gleaves@hotmail.com> on May 11th, 2008




"Alexander Grigoriev" wrote:

http://msdn.microsoft.com/en-us/library/ms810627.aspx

I want to stress that this mechanism we use is an option, if the
designer/coder that uses our API doesn't want the perf hit of doing this,
then they don't need to, it is just an option that they can use to afford
them the most protection, usually during dev/testing.

It is also worth stressing, that the access exception occurs only once (per
user API call) because once the page is set to ReadWrite inside the API lib,
it wont incur any more access exceptions, until after the API chain
ultimately returns to the caller, so it isn't as "bad" a hit as it may at
first seem.

Hugh



Posted by Maxim S. Shatskih on May 12th, 2008


This is not compatible with multithreading.

Non-multi-threaded API libraries modern days are just plain obsolete.

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


Posted by Alexander Grigoriev on May 12th, 2008



"Hugo gleaves@hotmail.com>" <hugh<underbar> wrote in message
news:2279C281-3578-46A5-9577-D496F3A85D20@microsoft.com...
I'm afraid you've chosen a wrong solution for a wrong problem brought up by
wrong solution.

If I'm not mistaken, you share memory between user mode code and kernel
mode. I'd advise you to be fully aware that no matter how you try, you
cannot trust any data in that memory. Even if you change protection on the
fly, another thread running in that process might trash the data while it's
unprotected. I'm not sure why you want to share that memory then. Why not
just use kernel address space for that?

Trying to protect in-process data from rogue client of your DLL is an
exercise in futility. If you need your own trusted data, it should reside in
a different process, for example, a service.

The rule is simple. If you run in some context, you're at the mercy of other
code running in the same context. If you want otherwise, you need different
context (different process, or processor mode).



Posted by Hugo gleaves@hotmail.com> on May 12th, 2008




"Maxim S. Shatskih" wrote:

Can you explain? what is not compatible with multithreading? If there is
anything that will prevent this from working reliably, do explain it will be
useful to know.

So far as page access rights go, these are (as you know) per-process, every
process has its own access rights to its VM pages (even shared pages have
per-process access rights).

The only area of uncertainty for me (I hope!) is the behavior of
InterlockedXXX on a datum in a ReadOnly page.

Thanks
Hugh



Posted by Hugo gleaves@hotmail.com> on May 12th, 2008




"Pavel A." wrote:

I think your assessment is correct, I have created some test code and run
this as both a 32-bit app and a 64-bit app under Vista.

The code uses an interlocked LONG situated in a memory mapped page that has
its access set to Read only. A vectored exception handler runs whenever an
attempt is made to call InterlockedCompareExchange, the handler checks that
the exception is an access violation and that the attempted operation was a
write to the expected page and sets the page to ReadWrite and continues
execution.

The test code aquires the lock then does some random updated to the shared
page, sleeps for a while, checks that nothing has changed and then reverses
the updates, unlocks the lock and resets the page to ReadOnly.

Running several instances of this on a dual-core x64 box seems fine (if the
code sees any unexpected change to data it will report an error) even for
hours, so I think that the interlock behaves reliably even if interrupted.

Thx for reply.


Posted by Alexander Grigoriev on May 12th, 2008



"Hugo gleaves@hotmail.com>" <hugh<underbar> wrote in message
news:AF8E146F-451D-4152-959F-D38571892327@microsoft.com...
Once again, this is an exercise in futility. Wrong and unnecessary solution
to a non-existing problem. It doesn't make sense to try to protect
in-process data from a mis-behaving client. If you want to protect shared
data, it should not be in the untrusted process; It needs to be in a
separate trusted process.

In your reply, the key word is "seems fine". So many times it's used in
context: "I implemented this kludge, it seems to work fine on my particular
machine, so it's good for production".




Posted by Maxim S. Shatskih on May 12th, 2008


- 2 threads wants to write access the same data
- first gain the access
- second thread gains the access
- first thread does its work and returns
- on return, the data is marked back as read-only
- then second thread try to do work, and the data is read-only, a crash

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


Posted by m on May 12th, 2008


More importantly, the 'protection' that this scheme offers does not exist
for a period of time when the API call is executing and so any 'rogue' code
can just write to the page then and it will be trashed.



Maxim's situation could be remedied by handling the second exception or
using locks on the APIs to prevent concurrent access to pages.


"Maxim S. Shatskih" <maxim@storagecraft.com> wrote in message
news:e%23jB2tHtIHA.4912@TK2MSFTNGP03.phx.gbl...


Posted by David Craig on May 12th, 2008


Also the 'multithreading' can easily be fixed just by raising your IRQL to
dispatch level. In user mode you might be able to increase priority enough
to get control. However, this won't fix the multiprocessor problems. In
kernel mode only spinlocks can be used to control access to data or hardware
so that only one thread on one processor can have exclusive control.

"m" <m@b.c> wrote in message news:et5i0AItIHA.4544@TK2MSFTNGP04.phx.gbl...


Posted by David Craig on May 13th, 2008


I should have said that in user mode you might be able to keep control in
one thread by going to realtime, but it is certain it will always work.
Programming for multiprocessor is always multithread safe, so do it in a way
it will be safe for SMP. Many current CPUs are SMP with multiple cores and
even the old hyperthreading simulates SMP but it was not quite as good as
having real multiple cores in a system. There are also tricks with
processor affinity in user mode that can help, but that type of code can be
fragile and hard to maintain - so why do it! Use a driver and spinlocks to
control access to low level resources. To protect data in user mode just
use semaphores, mutexes, and/or events as appropriate. Since this is not a
user mode code group, it is not really a good question for this newsgroup if
that is your target.

"David Craig" <drivers@nowhere.us> wrote in message
news:ubwXsmItIHA.1316@TK2MSFTNGP06.phx.gbl...


Posted by Pavel A. on May 13th, 2008


"Alexander Grigoriev" <alegr@earthlink.net> wrote in message
news:#9zNCzDtIHA.4952@TK2MSFTNGP05.phx.gbl...
Why? As long as somebody pays for all this work and time, it's ok...

--PA



Posted by Hugo gleaves@hotmail.com> on May 13th, 2008




"Maxim S. Shatskih" wrote:


The scenario you outline is correct, but the code isnt designed that way,
you have outlined an issue that a designer must address, but this is not an
incompatibility.

In reality the first thread doesnt reset page access to ReadOnly, the reset
only takes place if the returning thread is the last thread (this is tracked
in a count managed by a critical section).

All of this actually works, and was tested some time back (but the feature
has been dormant and unused for some time, but will be an officially
supported option for users of the API).

Thanks for outlining the issue though, it is a real issue and would be a
design need.

HUgh


Posted by Hugo gleaves@hotmail.com> on May 13th, 2008




"m" wrote:

window during which one thread has enabled write access (but only to the
pages it is touching) whilst another thread, not inside the API, erroneously
writes to one of the pages and causes damage.

This is sadly beyond our reach as a goal, but nevertheless the whole feature
is an option and mainly one that new users code will use during testing,
although there is a window I think the benefit afforded far outweighs this.

I think that in a future design, it might be possible to locate "some" of
the API inside some sort of kernel mode component but my knowledge of device
drivers (especially when used in such a "utility" fashion) is not very good.

Windows unfortunately provides no "out of the box" way for programmers to
write application code that can enjoy the same benefits as drivers (i.e. the
context mode change that allows drivers to access/update kernel structures or
pages allocated in one of the kernel heaps).

Hugh


Posted by Hugo gleaves@hotmail.com> on May 13th, 2008




"David Craig" wrote:


The problem we face is a real one, and unfortunately Win32 provides some
exclusion support but not enough. It is not only device drivers that must
protect resources, we need to protect our own applications shared structures.

In our case we have an in-memory shared "database" that process actually map
into their address space. The memory contains sophisticated structures, just
like an OS as well as users data.

Obviously an errant thread in some users application could damage some
structure, literally trashing perhaps hundreds of gigs of data (screwing up a
list or index tree or something).

However a nag has always been the lack of a fast read/write lock that we
could use as a spinlock (or something analogous to for very briefly locked
data).

We now have a pretty decent Read/Write spinlock that is based on interlock
operations, this provides us with:

1) Far less performance cost than a Mutex (which has no read/write concept)
2) Permanence (if apps all crash the lock state is retained because data is
memory mapped)
3) Upgradability: If a thread has lock in read mode for a time, it can at
any point request a write lock on same lock, this will (eventually) be
granted.
4) Nesting (Mutexes also provide this)

Thus our API becomes more managable, one function might aquire a read lock,
then call some other function lower down. This might request a write lock on
same lock and so on.

Windows provides us with no such mechanism (unless we dive into kernel mode
driver stuff) and the cost of Mutexes (even if one did devise some for of
read/write support) is far greater than an interlock operation.

This has been tested heavily on a dual-core x64 machine, and seem to be
reliable.

The issue that arose a few days ago (or rather a question) was how such
interlock operations will behave if the datum is situated in a read-only page
as described earlier in my post.

Finally I agree, this forum is for kernel driver people, BUT I have tried in
vain to get expert insights into these issues in other forums and sadly, very
very few developers have any real experience or appreciation of the issues,
driver people always have a superb grasp, all of the comments here for
example betray a firm grasp of the issues, one a standard Win32 forum, many
would be ill-informed, I have tried.

Thanks for comments though!

Hugh