Date: | October 26, 2007 / year-entry #388 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20071026-00/?p=24683 |
Comments: | 12 |
Summary: | One customer traced a problem they were having to the way they were calling a function similar in spirit to this one: HGLOBAL CopyClipboardData(UINT cf) { HGLOBAL hglob = NULL; HANDLE h = GetClipboardData(cf); if (h) { void *p = GlobalLock(h); if (p) { SIZE_T size = GlobalSize(h); hglob = GlobalAlloc(GMEM_FIXED, size); if (hglob) {... |
One customer traced a problem they were having to the way they were calling a function similar in spirit to this one: HGLOBAL CopyClipboardData(UINT cf) { HGLOBAL hglob = NULL; HANDLE h = GetClipboardData(cf); if (h) { void *p = GlobalLock(h); if (p) { SIZE_T size = GlobalSize(h); hglob = GlobalAlloc(GMEM_FIXED, size); if (hglob) { CopyMemory(hglob, p, size); } GlobalUnlock(h); } } return hglob; } This function takes a clipboard format and looks for it on the clipboard. If found, it returns a copy of the data. Looks great, huh?
The problem is that the customer would sometimes call the function as
The question from the customer:
You already know the answer to this.
But once in a rare while, the bitmap handle happens to smell just
enough like a fixed global handle that it passes the tests,
and The bitmap handle is basically a random number from the global heap's point of view, since it's just some number that some other component made up. It's not a global handle. If you generate enough random numbers, eventually one of them will look like a valid parameter. |
Comments (12)
Comments are closed. |
> We expected that the call to GlobalLock would fail with an invalid parameter error,
So excuse me, if they EXPECTED it to [always] fail, why bothering writing the then-branch??? *mortally confused*
Was there some special bitmap size and format involved which made HBITMAP look like HGLOBAL or it was truly random?
what should ordinary man do to get support like this from you Raymond (be a friend with Bill Gates maybe)? :)
OMG!
What a scary sentence: "… usually fails the quick tests performed by GlobalLock …"!?
I have code that looks like this:
void somefunction (HANDLE theHandle)
{
UINT retVal = GlobalFlags (theHandle);
if (retVal == GMEM_INVALID_HANDLE)
return;
…
}
Is this something I can count on? Or does it fall under "quick, but unreliable" tests?
IgorD: I might not be Raymond, but I’m certainly highly suspicious of your code. Even without the problem that Raymond is talking about here (e.g. if all the handles you’re testing for validity originally came from the global heap), I don’t think your code can possibly work reliably. Consider what happens, for example, when a handle that you were previously using is reallocated to some other purpose.
I’d definitely suggest restructuring so an invalid handle can never get this far.
In this article Raymond Chen, the creator of Windows NT, talks about…
Jules, maybe you’re right but I cant follow.
Most of my functions perform checks at the beginning – if pointer is not NULL or if index is within a range. Here I try to check if passed handle was really a global handle, one created with GlobalAlloc() so I can pass it few lines later to GlobalLock().
And that was my question – is GlobalFlags() reliable to recognize such handles and will it ALWAYS return GMEM_INVALID_HANDLE for handles that were not created with GlobalAlloc()?
IgorD: The MSDN documentation is fairly explicit on what it does and what parameters it assumes: "The GlobalFlags function returns information about the specified global memory object." And then in the Parameters paragraph: "Handle to the global memory object. This handle is returned by either the GlobalAlloc or GlobalReAlloc function."
Besides being provided only for compatibility with 16-bit versions of Windows, GlobalFlags() is most definately NOT meant to be used to verify the integrity of random input. If you don’t know what you’re passing into your function it is essentially random data.
.t
IgorD said: "Here I try to check if passed handle was really a global handle, one created with GlobalAlloc() so I can pass it few lines later to GlobalLock()."
Let me restate what others (including Raymond) have already said.
You cannot check if passed handle is really a global handle. You either know it is (because you called the function with a handle you got from GlobalAlloc()) or you don’t. There is no way to be sure, because handle is after all just a number.
OK, I see the message!
In my case, I receive a HGLOBAL handle either from GlobalAlloc() or GetClipboardData() and if it’s OK then it is OK. If it turns out to be a dud, then there’s nothing I could do about it.
I’ll comment out GlobalFlags() call (and leave just the NULL test) and see what happens. I suppose, nothing remarkable should happen anyway ;)
Thanks everybody for your patience!
IgorD said : "In my case, I receive a HGLOBAL handle either from GlobalAlloc() or GetClipboardData()"
No you don’t.
You receive HGLOBAL from GlobalAlloc(), but you receive random number from GetClipboardData() (since depending on the format asked it can return HBITMAP or something else). That was the point of Raymond’s article.
Just blindly attempting GlobalLock() won’t save you either, because it may succeed even if you passed a value which is not HGLOBAL.
Maybe I wasn’t clear in my forst post, but I do not have one generic function responsible for all kinds of clipboard types, so I do not have anything like Raymond’s example.
In other words, since I call GetClipboardData() only with my own types recieved from RegisterClipboardFormat() and all I send to SetClipboardData() are global handles I think I’m safe.