December, 2003

  • The Old New Thing


    Last night I had to frog several dozen rows of knitting because I forgot to change needles. Color changes I remember. Needle changes I always forget. Probably because color changes are much more exciting.
  • The Old New Thing

    How to void your warranty


    MSDN just published an article telling people that it's okay to use reserved fields in an internal structure. Anybody who does this has just voided their warranty. Please put a "This program has a high likelihood of crashing after you install the next Service Pack or upgrade your OS" in your license agreement.

    And you wonder why app compat is so hard.

  • The Old New Thing

    What is the window nesting limit?


    In the old days, Windows didn't bother enforcing a nesting limit because, well, if you want to nest windows 200 deep, that's your decision. Many window operations are recursive, but since everything happened on the application's stack, it was your own responsibility to make your stack big enough so it didn't overflow.

    But Windows NT moved the window manager off the application stack (first into a separate process, then into kernel mode). So now the OS needs to watch out for stack overflow attacks from people creating too many nested windows.

    The window nesting limit was set to 100 for the early days of Windows NT. For Windows XP, it dropped to 50 because increased stack usage in some internal functions caused us to overflow at around 75. Dropping to 50 created some breathing room.

    Disclaimer: I was not personally involved in this issue. I'm just reporting what I was able to figure out from reading checkin logs.

  • The Old New Thing

    When marketing designs a screenshot


    Have you checked out AMD's ads for their AMD Athlon 64 FX processor? The copy reads, "My adrenalin fix isn't what it used to be. Double the dose. AMD me." And it has a picture of a tough-looking guy glaring at the camera, challenging the reader to do their worst.

    But take a look at what's on his screen. It's a giant command window. Now look at what this adrenalin junkie has been typing into his command window:

    Microsoft Windows XP [Version 5.1.2600]
    (C) Copyright 1985-2001 Microsoft Corp.
    'fg' is not recognized as an internal or external command,
    operable program or batch file.
    'sdf' is not recognized as an internal or external command,
    operable program or batch file.
    'sdf' is not recognized as an internal or external command,
    operable program or batch file.
    'df' is not recognized as an internal or external command,
    operable program or batch file.
    Wow, he's a real l33t h4x0r he iz.
  • The Old New Thing

    Tinkering with the look

    I've been tinkering with the look of the comments section, based on initial feedback. Took me a while but I think I found something that works pretty well.
  • The Old New Thing

    The unsafe device removal dialog


    In a comment, somebody asked what the deal was with the unsafe device removal dialog in Windows 2000 and why it's gone in Windows XP.

    I wasn't involved with that dialog, but here's what I remember: The device was indeed removed unsafely. If it was a USB storage device, for example, there may have been unflushed I/O buffers. If it were a printer, there may have been an active print job. The USB stack doesn't know for sure (those are concepts at a higher layer that the stack doesn't know about) - all it knows is that it had an active channel with the device and now the device is gone, so it gets upset and yells at you.

    In Windows XP, it still gets upset but it now keeps its mouth shut. You're now on your honor not to rip out your USB drive before waiting two seconds for all I/O to flush, not to unplug your printer while a job is printing, etc. If you do, then your drive gets corrupted / print job is lost / etc. and you're on your own.
  • The Old New Thing

    The migration continues

    Hey folks, this blog is moving to a new home. It'll take me a while to set up shop there, though, so please be patient. Some time after the new year, the content will likely migrate to the new location.
  • The Old New Thing

    Welcome to the New Old New Thing

    Hey there, everybody. It's going to take me a while to settle in, so please be patient. It seems I always celebrate a new blog by designing some insane multi-part series on some obscure aspect of Win32, so I think I'll welcome this site with a series of articles that demonstrate various things you can do with the shell namespace. I haven't planned it out yet, though, so I don't know how many parts it'll be or how much I'll manage to cover before I get bored.
  • The Old New Thing

    Tote Hose in Weilburg


    Wenn Teenies shoppen gehen: Flugzeuge im Warenkorb. This article caught my eye because it opens with a German slang phrase I learned just a few weeks ago: "Tote Hose" which means roughly "Absolutely nothing doing". Here's an English version of the article for those whose German isn't quite up to snuff.

    A question for Germans: Since when did "shoppen" become a German word? What happened to "einkaufen"? Do people prefer the English words because they sound "cooler"?
  • The Old New Thing

    Why are structure sizes checked strictly?


    You may have noticed that Windows as a general rule checks structure sizes strictly. For example, consider the MENUITEMINFO structure:

    typedef struct tagMENUITEMINFO {
      UINT    cbSize; 
      UINT    fMask; 
      UINT    fType; 
      UINT    fState; 
      UINT    wID; 
      HMENU   hSubMenu; 
      HBITMAP hbmpChecked; 
      HBITMAP hbmpUnchecked; 
      ULONG_PTR dwItemData; 
      LPTSTR  dwTypeData; 
      UINT    cch; 
    #if(WINVER >= 0x0500)
      HBITMAP hbmpItem; // available only on Windows 2000 and higher

    Notice that the size of this structure changes depending on whether WINVER >= 0x0500 (i.e., whether you are targetting Windows 2000 or higher). If you take the Windows 2000 version of this structure and pass it to Windows NT 4, the call will fail since the sizes don't match.

    "But the old version of the operating system should accept any size that is greater than or equal to the size it expects. A larger value means that the structure came from a newer version of the program, and it should just ignore the parts it doesn't understand."

    We tried that. It didn't work.

    Consider the following imaginary sized structure and a function that consumes it. This will be used as the guinea pig for the discussion to follow:

    typedef struct tagIMAGINARY {
      UINT cbSize;
      BOOL fDance;
      BOOL fSing;
      // v2 added new features
      IServiceProvider *psp; // where to get more info
    // perform the actions you specify
    STDAPI DoImaginaryThing(const IMAGINARY *pimg);
    // query what things are currently happening
    STDAPI GetImaginaryThing(IMAGINARY *pimg);

    First, we found lots of programs which simply forgot to initialize the cbSize member altogether.

    IMAGINARY img;
    img.fDance = TRUE;
    img.fSing = FALSE;

    So they got stack garbage as their size. The stack garbage happened to be a large number, so it passed the "greater than or equal to the expected cbSize" test and the code worked. Then the next version of the header file expanded the structure, using the cbSize to detect whether the caller is using the old or new style. Now, the stack garbage is still greater than or equal to the new cbSize, so version 2 of DoImaginaryThing says, "Oh cool, this is somebody who wants to provide additional information via the IServiceProvider field." Except of course that it's stack garbage, so calling the IServiceProvider::QueryService method crashes.

    Now consider this related scenario:

    IMAGINARY img;

    The next version of the header file expanded the structure, and the stack garbage happened to be a large number, so it passed the "greater than or equal to the expected cbSize" test, so it returned not just the fDance and fSing flags, but also returned an psp. Oops, but the caller was compiled with v1, so its structure doesn't have a psp member. The psp gets written past the end of the structure, corrupting whatever came after it in memory. Ah, so now we have one of those dreaded buffer overflow bugs.

    Even if you were lucky and the memory that came afterwards was safe to corrupt, you still have a bug: By the rules of COM reference counts, when a function returns an interface pointer, it is the caller's responsibility to release the pointer when no longer needed. But the v1 caller doesn't know about this psp member, so it certainly doesn't know that it needs to be psp->Release()d. So now, in addition to memory corruption (as if that wasn't bad enough), you also have a memory leak.

    Wait, I'm not done yet. Now let's see what happens when a program written in the future runs on an older system.

    Suppose somebody is writing their program intending it to be run on v2. They set the cbSize to the larger v2 structure size and set the psp member to a service provider that performs security checks before allowing any singing or dancing to take place. (E.g., makes sure everybody paid the entrance fee.) Now somebody takes this program and runs it on v1. The new v2 structure size passes the "greater than or equal to the v1 structure size" test, so v1 will accept the structure and Do the ImaginaryThing. Except that v1 didn't support the psp field, so your service provider never gets called and your security module is bypassed. Now everybody is coming into your club without paying.

    Now, you might say, "Well those are just buggy programs. They deserve to lose." If you stand by that logic, then prepare to take the heat when you read magazine articles like "Microsoft intentionally designed <Product X> to be incompatible with <software from a major competitor>. Where is the Justice Department when you need them?"
Page 3 of 5 (45 items) 12345