You can name your car, and you can name your kernel objects, but there is a qualitative difference between the two


A customer reported that the Wait­For­Single­Object appeared to be unreliable.

We have two threads, one that waits on an event and the other that signals the event. But we found that sometimes, signaling the event does not wake up the waiting thread. We have to signal it twice. What are the conditions under which Wait­For­Single­Object will ignore a signal?

// cleanup and error checking elided for expository purposes

void Thread1() { // Create an auto-reset event, initially unsignaled HANDLE eventHandle = CreateEvent(NULL, FALSE, FALSE, TEXT("MyEvent"));

// Kick off the background thread and give it the handle CreateThread(..., Thread2, eventHandle, ...);

// Wait for the event to be signaled WaitForSingleObject(eventHandle, INFINITE); }

DWORD CALLBACK Thread2(void *eventHandle) { ResetEvent(eventHandle); // start with a clean slate DoStuff(); // All the calls to SetEvent succeed. SetEvent(eventHandle); // this does not always wake up Thread1 SetEvent(eventHandle); // need to add this line return 0; }

Remember, you generally shouldn't start with the conspiracy theory. The problem is most likely close to home.

People offered a variety of theories as to what may be wrong. One possibility is that some other code in the process is calling Reset­Event on the event handle. Another is that some other code in the process has a bug where it is calling Reset­Event on the wrong event handle.

I asked about the name.

I have a friend who names her car. Whenever she gets a new car, she agonizes over what to call it. She'll drive it for a few days to see what its personality is and eventually choose a name that suits the vehicle. And thereafter, whenever she refers to her car, she uses the name. (She also assigns the car a gender.)

If you like naming your car, then that's great. But there's a difference between naming your car and naming your kernel objects. When you give your car a name, that name is just for your private use. On the other hand, if you give your kernel object a name, other people can use that name to access your object. And once they have access to your object, they can do funky things to it, like reset it.

Imagine if you decided to name your car Clara, and any time somebody shouted, "Clara, where are you?" your car horn honked. I'm assuming your car has voice recognition software. Also that your car has the personality of a puppy. Work with me here.

Even scarier: Any time somebody shouted, "Clara, open the trunk," your car trunk unlocked.

That's what happens when you name your kernel objects. Anybody who knows the name (and has appropriate access) can open the object and start doing things to it. Presumably that's why you named your kernel object in the first place: You want this to happen. You gave your object a name specifically to allow other people to come in and access the same object.

In the above example, I saw that the event had a very generic-sounding name, My­Event. That sounds like the name that some other similarly uncreative application developer might have chosen.

And indeed, that was the reason. There was another application which was creating an event that coincidentally has the same name, so instead of creating a new object, the kernel returned a handle to the existing one. The other application called Wait­For­Single­Object on the event, and so when the customer's program called Set­Event, it woke the other application instead. So this bug has a double-whammy: Not only does it cause your program to miss a signal, it causes the other program to receive a signal when it wasn't expecting one. Two bugs for the price of one.

Note that no matter how clever you are at choosing a name for your event, you will always have this problem, because even if you called it Super­Secret­Never­Gonna­Find­It­75, there's a program out there that knows the secret name: Namely your own program! If you run two copies of your program, they will both be manipulating the same Super­Secret­Never­Gonna­Find­It­75, and then you're back where you started. When the first copy of the program calls Set­Event, it may wake up the second copy.

(This is the same principle behind the conclusion that a single-instance program is its own denial of service.)

Kernel objects should not be named unless you intend them to be shared, because once you name them, you open yourself to issues like this. If you name a kernel object, it must be because you want another process to access it, not because you think giving it a name is kind of cute.

I suspect a lot of people give their kernel objects names not because they intend them to be shared, but because they see that the Create­Event function has a lpName parameter, and they think, "Well, I guess giving it a name would be nice. Maybe I can use it for debugging purposes or something," not realizing that giving it name actually introduced a bug. Another possibility is that they see that there is a lpName parameter and think, "Gosh, I must give this event a name."

Kernel object names are optional. Don't give them a name unless you intend them to be shared.

Comments (30)
  1. alegr1 says:

    CreateEvent/Mutex/etc documentation needs to have a note that says: DO NOT NAME THE OBJECT UNLESS YOU USE IT ACROSS PROCESSES!

    But then, again, nobody reads the documentation.

    [There are valid cases for naming objects for intra-process use, so the note would be incorrect. The important thing is that if a name is given, the object can be accessed by CreateXxx or OpenXxx. This is already mentioned in the documentation. -Raymond]
  2. Count Zero says:

    I'm kind of surprised that Raymond did not mention the kernel object namespaces which could also ease the troubles caused by unnecessary object naming.

  3. Medinoc says:

    When I name a kernel object for the explicit purpose of interprocess communication, whenever possible I try to generate a unique name and send it to the other process (through command line if I start it, or some other means of IPC if I don't). The name always includes the creator thread's ID, ensuring no other thread generates an identical name.

  4. Joker_vD says:

    @Medinoc: You really should include the thread's starting time, because thread IDs are reused. Also, you may consider appending a freshly generated GUID or two to the event's name, just to be sure (When I was tasked to come up with a name of a super-secret cookie, I glued three GUIDs together…. so far it seems to work).

  5. Count Zero says:

    @Joker_vD – One GUID should be more than enough. That is why they are called Globally Unique IDentifiers. Theoretically the chance of GUID collision in 2-3 million years is still less than something you can represent as a single precision float.

  6. Medinoc says:

    @Joker_vD: Usually I use the thread ID and the object creation time (plus some more values if there's a non-negligible possibility of the same thread creating two such objects in the same millisecond).

  7. Joshua says:

    I know, I'll name my kernel object Klutz.

    /ducks

  8. Gabe says:

    Medinoc: "Usually"? How often do you find yourself creating objects that need to be accessible outside of your process but without a well-known name? I'm having trouble contriving such a use case.

  9. Floyd says:

    @Gabe: Consider implementing an IPC mechanism for exclusive use by two or more of your processes, that signal available data by means of name kernel objects. These objects need names so that they can be shared between participating processes. The names, however, should not be well-know, to limit access.

  10. John Ludlow says:

    Has the friend in this story watched Herbie movies a few too many times?

  11. Brian_EE says:

    @Joshua, I think Klink would be funnier.

  12. lefty says:

    This is why I name all my kernel objects "GeorgeForeman".

  13. Roger says:

    "Don't give them a name unless you intend them to be shared."

    Good parenting advice too!

  14. use GUID in name says:

    Seems like if you simply include a GUID in the name, that should cut down on accidental name collision?

  15. 640k says:

    2^128 kernel names is enough for everyone.

  16. poizan42 says:

    Medinoc: Unless you are stuck with badly designed libraries *cough* .NET *cough*, you shouldn't pass the name of objects to your child processes for IPC – instead make a non-shareable (including non-inheritable) object and use DuplicateHandle to give your child process access to it.

  17. Horrible Person says:

    I want you to do a story about the Windows 9 name rumors, or at least cover some programs that are this lazy

    twitter.com/…/517358472715710465

    Please? Pretty Please?

    [Check out the second word in the title of the blog. Wait five years, then ask again. -Raymond]
  18. Yuhong Bao says:

    @Horrible Person: That is pretty simple, Java has code to translate the raw version info into marketing names:

    hg.openjdk.java.net/…/java_props_md.c

    Unfortunately, people managed to use the marketing names to create incorrect version checks, as mentioned above on twitter.

  19. poizan42 says:

    @Medinoc: Write it to the process's stdin?

  20. Is there a way to enumerate named events? I'm curious to see what the programs I have running are using.

  21. poizan42 says:

    @Maurits: NtQuerySystemInformation with the undocumented SystemHandleInformation (0x10) or SystemExtendedHandleInformation (0x40) value for SystemInformationClass.

    Usual disclaimers about using undocumented features applies ofc.

  22. OldFart says:

    Or, you can use the Sysinternals "handle" program to get a list of kernel objects.

  23. John Ludlow says:

    Or Process Explorer if you prefer a GUI

  24. Medinoc says:

    @poizan42: And then I have to communicate my new, duplicated handle to the child process, using some *other* means because I can no longer use the command line.

    Not a problem for a Windowed application (just SendMessage() it), but harder for a console-based app.

  25. JamesJohnston says:

    @Medinoc: And this is what the "bInheritHandle" member of the "SECURITY_ATTRIBUTES" structure passed to CreateEvent is for.  Naming the event is still wrong if you need an event sharable between your process and a child process.

  26. Carl D says:

    … Or you can use Resource Monitor (CPU tab – associated handles pane) if you want to use something built-in to Windows itself.

  27. ChrisR says:

    @JamesTJohnston: I'm not disagreeing that you don't need to name the event, but I don't think you got Medinoc's point.  The handle value itself still has to be communicated to the child process for the child process to know which inherited handle value it is supposed to use for the event.  This can be either through the command line, environment or other IPC according to MSDN [1].  So if you rule out the command line, as per poizan42, then you are left with environment or other IPC, which is what Medinoc is begrudging.  [1] msdn.microsoft.com/…/ms683463(v=vs.85).aspx

  28. Jasper says:

    That seems like an unfortunate choice in the API. I don't blame the original developers. I also would have expected that a method called "Create", would indeed, "Create" something.

  29. You can also use WinObj. It shows the object manager's namespace. That's where all the named kernel objects are kept.

  30. Joker_vD says:

    @Jasper: It does create a new handle, which has a brand new record in the process's handle table. Is it good enough?

Comments are closed.

Skip to main content