October, 2004

Larry Osterman's WebLog

Confessions of an Old Fogey
  • Larry Osterman's WebLog

    How does COM activation work anyway?

    • 6 Comments

    One of my co-workers came to me the other day and asked why on earth COM had this "dwClsContext" parameter. In particular, he was concerned about the various CLSCTX_XXX_SERVER options.

    In order to explain what the point of the CLSCTX_XXX_SERVER options, you understand how COM activation works. In general, there are two cases: CLSCTX_INPROC_SERVER and CLSCTX_LOCAL_SERVER (there’s a 3rd option, CLSCTX_INPROC_HANDLER, which is half-way between the two as well).

    So what does happen when you call CoCreateInstance() to instantiate a class? It turns out that this is documented with the documentation for the CLSCTX parameter (go figure that one out), but in a nutshell (ignoring several issues like COM object activation security), the following happens:

    For both cases, the first thing that COM does is to open HKEY_CLASSES_ROOT(HKCR)\CLSID\{<STRINGIZED-GUID-OF-RCLSID>}.

    If the user specified CLSCTX_INPROC_SERVER (or CLSCTX_INPROC_HANDLER), COM looks for an InprocServer32 key under the classid. If the InprocServer32 key is found, then the default value of the key specifies the DLL to be loaded. The ThreadingModel value in that key specifies the desired threading model for the object. If the threading model for the object is compatible with the threading model for the thread (see "What are these "Threading Models" and why do I care?" for more info on threading models), then the DLL is loaded. Once the DLL’s been loaded, COM calls GetProcAddress() to find the address of the DllGetClassObject routine for the DLL. The DllGetClassObject routine’s job is to return an instance of an object that implements IClassFactory for all the classes supported by that DLL.

    If the user specified CLSCTX_LOCAL_SERVER, it means that the client wants to contact an out-of-proc COM server. For an out-of-proc object, there are a gain two possible choices – the COM server could be implemented by an NT service, or it could be implemented by a local executable.

    In both cases, COM needs to know activation information about the object, so it looks to see if there’s an APPID value associated with the class. If there IS an APPID value, then COM opens HKCR\APID\{<STRINGIZED-GUID-OF-APPID>}. Under the APPID key, a number of values are located – the first is the AccessPermission value, which contains the security descriptor that’s used to determine if the caller is allowed to access the COM object. The second is the LaunchPermission, which determines if the application is allowed to load the class factory for this class. Also located under the APPID is the RunAs key which specifies the security principal under which the server should be run. At some point in the future I’ll talk about these keys, but they’re beyond the scope of this article. The last piece of information that’s retrieved from the APPID is the LocalService value, which indicates the service that will handle the COM object.

    To handle the first case, the first thing that COM does is to look for a LocalService key. If the LocalService Key is found (or if a LocalService key was found under the APPID), then COM attempts to start the service specified (if it’s not already started).

    If there’s no LocalService key, then, COM looks for the LocalServer32 key. If it finds the key, again, the default value of the key specifies an executable name, and the ThreadingModel value specifies the threading model for the object. COM then launches the application (this is where COM checks for CLSCTX_DISABLE_AAA and CLSCTX_ENABLE_AAA).

    Once the service (or executable) comes up, it calls CoRegisterClassObject(). The call to CoRegisterClassObject informs the DCOM service (via RPC) of the address of a class factory for a particular ClassID.

    In both out-of-proc cases, the COM client next attempts to connect to the DCOM service, which looks up the class object in its table of class factories and returns a pointer to that class factory to the client.

    Now that the client has a class factory for the COM object (either by retrieving it locally or via the DCOM service), the client can call into the class factory to retrieve an instance of the class in question. It calls IClassFactory::CreateInstance which will then instantiate the object for the application. And finally CoCreateInstance will return that pointer to the client application.

    One thing to note: some COM objects have both in-proc and out-of-proc implementations (NetMeeting is an example of this). In those cases, a client could request that the service run either in OR out-of-proc, transparently.

    Oh, one thing I didn’t mention above is the CLSCTX_ALL definition, which is declared in objbase.h – CLSCTX_ALL is simply a combination of CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER – it tries all the above and goes with the one that works

     

  • Larry Osterman's WebLog

    What IS audio on a PC anyway?

    • 39 Comments

    This may be well known, but maybe not (I didn’t understand it until I joined the Windows Audio team).

    Just what is digital audio, anyway?  Well, at its core, all of digital audio is a “pop” sound made on the speaker.  When you get right down to it, that’s all it is.  A “sound” in digital audio is a voltage spike applied to a speaker jack, with a specific amplitude.  The amplitude determines how much the speaker diaphragm moves when the signal is received by the speaker.

    That’s it, that’s all that digital audio is – it’s a “pop” noise.  The trick that makes it sound like Sondheim is that you make a LOT of pops every second – thousands and thousands of pops per second.  When you make the pops quickly enough, your ear puts the pops together to turn them into a discrete sound.  You can hear a simple example of this effect when you walk near a high voltage power transformer.  AC power in the US runs at 60 cycles per second, and as the transformer works, it emits a noise on each cycle.  The brain smears that 60 Hz sound together and turns it into the “hum” that you hear near power equipment.

    Another way of thinking about this (thanks Frank) is to consider the speaker on your home stereo.  As you’re listening to music, if you pull the cover off the speaker, you can see the cone move in and out with the music.  Well, if you were to take a ruler and measure the displacement of the cone from 0, the distance that it moves from the origin is the volume of the pop.  Now start measuring really fast – thousands of times a second.  Your collected measurements make up an encoded representation of the sound you just heard.

    To play back the audio, take your measurements, and move the cone the same amount, and it will reproduce the original sound.

    Since a picture is worth a thousand words, Simon Cooke was gracious enough to draw the following...

    Take an audio signal, say a sine wave:

    Then, you sample the sine wave (in this case, 16 samples per frequency):

    Each of the bars under the sine wave is the sample.  When you play back the samples, the speaker will reproduce the original sound.  One thing to keep in mind (as Simon commented) is that the output waveform doesn't look quite like the stepped function that the samples would generate.  Instead, after the Digital-to-Audio-Converter (DAC) in the sound card, there's a low pass filter that smooths the output of the signal.

    When you take an analog audio signal, and encode it in this format, it’s also known as “Pulse Coded Modulation”, or “PCM”.  Ultimately, all PC audio comes out in PCM, that’s typically what’s sent to the sound card when you’re playing back audio.

    When an analog signal is captured (in a recording studio, for example), the volume of the signal is sampled at some frequency (typically 44.1 kHz for CD audio).  Each of the samples is captured with a particular range of amplitudes (or quantization).  For CD audio, the quantization is 16 bits, in two samples.  Obviously, this means that each sample has one of at most 65,536 values, which is typically enough for most audio applications.  Since the CD audio is stereo, there are two 16 bit values for each sample. 

    Other devices, like telephones, on the other hand, typically uses 8 bit samples, and acquires their samples at 8kHz – that’s why the sound quality on telephone communications is so poor (btw, telephones don’t actually use direct 8 bit samples, instead their data stream is compressed using a format called mu-law (or a-law in Europe), or G.711).  On the other hand, the bandwidth used by typical telephone communication is significantly lower than CD audio – CD audio’s bandwidth is 44,100*16*2=1.35Mb/second, or 176KB/second.  The bandwidth of a telephone conversation is 64Kb/second, or 8KB/second (reduced to from 3.2Kb/s to 11Kb/s with compression), an order of magnitude lower.  When you’re dealing with low bandwidth networks like the analog phone network or wireless networks, this reduction in bandwidth is critical.

    It’s also possible to sample at higher frequencies and higher sample sizes.  Some common sample sizes are 20bits/sample and 24bits/sample.  I’ve also seen 96.2 kHz sample frequencies and sometimes even higher.

    When you’re ripping your CDs, on the other hand, it’s pointless to rip them at anything other than 44.1 kHz, 16 bit stereo, there’s nothing you can do to improve the resolution.  There ARE other forms of audio that have a higher bit rate, for example, DVD-Audio allows samples at 44.1, 48, 88.2, 96, 176.4 or 192 kHz, and sample sizes of 16, 20, or 24 bits/sample, with up to 6 96 kHz audio channels or 2 192 kHz samples.

    One thing to realize about PCM audio is that it’s extraordinarily sparse – there is a huge amount of compression that can be done to the data to reduce the size of the audio data.  But in most cases, when the data finally hits your sound card, it’s represented as PCM data (this isn’t always the case, for example, if you’re using the SPDIF connector on your sound card, then the data sent to the card isn’t PCM).

    Edit: Corrected math slightly.

    Edit: Added a couple of pictures (Thanks Simon!)

    Edit3: Not high pass, low pass filter, thanks Stefan.

  • Larry Osterman's WebLog

    IIS vs Apache vulnerabilities chart.

    • 15 Comments

    Michael Howard just posted this analysis of relative vulnerabilities between IIS6 and Apache 2.0 during the year after their respective releases.

    A fascinating post, and it goes to show that maybe we really DO understand what we're doing.

    Edit: I make this post, and what does Michael do?  He posts a second set of charts :)

  • Larry Osterman's WebLog

    Oh wow, Bruce Schneier's blogging.

    • 8 Comments

    I just discovered that Bruce Schneier, author of Applied Cryptography is blogging.  His blog (including a full content RSS 1.0 feed) can be found here.

    Bruce's one of those people I truely respect in security, he's been highly critical of Microsoft in the past (for reason), but boy, he can write.

    Subscribed.

    Edit: To give credit where it's due, I found this here on forgey's blog.

     

  • Larry Osterman's WebLog

    So what exactly IS COM anyway?

    • 29 Comments

    A couple of days ago, David Candy asked (in a comment on a previous COM related post) what exactly was COM.

    Mike Dimmick gave an excellent answer to the question, and I'd like to riff on his answer a bit.

    COM is just one of three associated technologies: RPC, COM and OLE (really OLE Automation).

    Taken in turn:

    RPC, or Remote Proceedure Call, is actually the first of the "Cairo" features to debut in Windows (what, you didn't know that there were parts of Cairo already in Windows?  Yup, actually, almost all of what was called "Cairo" is currently in windows).

    RPC provides a set of services to enable inter-procedure and inter-machine procedure calls.  The RPC technology is actually an implementation of the DCE RPC specification (the DCE APIs are renamed to be more windows-like), and is on-the-wire interoperable with 3rd party DCE implementations.  RPC deals with two types of entities, client's and servers.  The client makes requests, and the server responds to those requests.  You tell RPC about the semantics of the procedures you're calling with an IDL file (IDL stands for "Interface Definition Language" - It defines the interface between client and server).  IDL files are turned into C files by MIDL, the "Microsoft IDL compiler".

    When RPC needs to make a call from one process to another, it "marshalls" the parameters to the function call.  Marshalling is essentially the process of flattening the data structures (using the information in the IDL file), copying the data to the destination and then unpacking the flattened data into a format that the receiver can use.

    RPC provides an extraordinarily rich set of services - it's essentially trivial to write an application that says "I want to talk to someone on my local network segment who's providing this service, but I don't care who they are - find out who's offering this service and let me talk to them" and RPC will do the hard work.

    The next technology, COM, is built on RPC.  COM stands for "Component Object Model".  COM is many, many, things - it's a design pattern, it's a mechanism to hide implementation of functionality, it's an inter-process communication mechanism, it's the kitchen sink.

    At it's heart, COM's all about a design pattern that's based around "Interfaces".  Just as RPC defines an interface as the contract between a client and a server, COM defines an interface as a contract between a client of a set of functionality and the implementor of that functionality.  All COM interfaces are built around a single "base" interface called IUnknown, which provides reference count semantics, and the ability to query to see if a particular object implements a specific interface.  In addition, COM provides a standardized activation pattern (CoCreateInstance) that allows the implementation of the object to be isolated from the client of the object. 

    Because the implementation of the COM object is hidden from the client of the object, and the implementation may exist in another process (or on another machine in the case of DCOM), COM also defines its interfaces in an IDL file.  When the MIDL compiler is compiling an IDL file for COM, it emits some additional information including a C++ class definitions (and C surrogates for those definitions).  It will also optionally emit a typelib for the interfaces.

    The typelib is essentially a partially compiled version of the information in the IDL - it contains enough information to allow someone to know how to marshall the data.  For instance, you can take the information in a typelib and generate enough information to allow managed code to interoperate with the COM object - the typelib file contains enough information for the CLR to know how to convert the unmanaged data into its managed equivilant (and vice versa).

    The third technology is OLE Automation (Object Linking and Embedding Automation).  OLE Automation is an extension of COM that allows COM to be used by languages that aren't C/C++.  Essentially OLE Automation is built around the IDispatch interface. IDispatch can be though of as "varargs.h-on-steroids" - it provides a abstraction for the process of passing parameters too and from functions, thus allowing an application to accept method semantics that are radically different from the semantics provided by the language (for instance, VB allows parameters to functions to be absent, which is not allowed for C functions - IDispatch allows a VB client to call into an object implemented in C).

    Anyway that's a REALLY brief discussion, there are MANY, MANY books written about this subject.  Mike referenced Dale Rogerson's "Inside COM", I've not read that one, but he says it's good :)

     

     

  • Larry Osterman's WebLog

    What's in a WAV file?

    • 11 Comments

    So yesterday, I mentioned that a WAV file is just a wrapper around raw PCM data.  Well, that’s not entirely accurate (it’s not inaccurate either, but…)

    A WAV file is in fact a RIFF file, that contains waveform (discrete PCM audio samples) content.  RIFF (Resource Interchange File Format) is the file format that Microsoft historically has used to encode much of its multimedia content (WAV, AVI, etc).  The RIFF format describes “chunks” of data, which can be strung together to make a single multimedia title.

    Windows provides APIs for manipulating RIFF files, they’re the “mmio” family of APIs exported from winmm.dll.  There are APIs for opening, reading and writing to RIFF files, and for parsing the various chunks of data in the file (each chunk can in turn contain multiple chunks).

    A WAV file is a RIFF file that contains two chunks.  The first chunk is named “fmt”, and the other is named “data”.

    The “fmt” chunk contains a WAVEFORMAT which describes the format of the data contained in the “data” chunk.

    The “data” chunk contains the PCM data for the audio sample.

    There’s actually a partial example of parsing a RIFF file on MSDN, which shows how to navigate through a RIFF file.

    If you’re not interested in using the mmio APIs to write (or parse) RIFF files, then I discovered this page at Stanford that describes the format of a WAVE file as a flat file.

  • Larry Osterman's WebLog

    Innovation in the computer industry

    • 38 Comments

    The germ of this post was a comment I made on Robert Scoble's weblog, but it deserves broader distribution (that also explains why it's sort-of two ideas mashed together) :)

    One of the memes that I see going on in the boards (especially on /.) is that Microsoft never innovates; it only ever copies what others have done before.  I find this meme rather fascinating because it is SO wrong.  The thing is that the people who cite this meme set the bar impossibly high - by their definition, if an idea isn't the very first example of an idea, then it was copied, and thus wasn't innovative.

    The problem is that if the bar for "innovative" is that nobody has ever done it before, than the list of "innovations" in the PC industry is very, very, very small - the web is a repackaging of hypertext (HTML is a stripped down SGML), the MacIntosh was done before on the Lisa (which was done before on the Star (which was done before on the Perq (which was done before on the Alto))).

    In the past 25 years, I can only think of two or three truly innovative ideas that weren't rehashes of previous ideas:
          Spreadsheets
          DNS
          Clippy/Bob. (Don't laugh. Innovative doesn't necessarily mean "a good idea" - it means a NOVEL idea. I believe that Bob was the first shipping social UI (I may be wrong on this one)).

    Actually, the more I think about it, the number of things that are truly original is even smaller than I thought.

    For instance GUI operating systems like the Mac or Windows don't count, since there were GUI operating systems more than 25 years ago (the Perq as mentioned above).

    P2P filesharing like Kazaa or Gnutella don't count as innovative, because they are simply the Windows for Workgroups network browser extended to content and scaled up to the Internet, and WfW was just an extension of the Lan Manager browser made peer-to-peer.  Btw, to answer someone who posted in the Scoble comment thread: pure file sharing services like FTP have been around forever, but they have a discoverability issue.  The thing that makes a distributed data store (like file sharing services or the WWW) successful is the directory – consider what finding something on the web would be like without Google or its cousins.

    CD's and DVD's don't count, they are just another storage medium based on older technologies (and CDs are 22 years old anyway).

    Gnu/Linux isn't innovative by this definition; they're just rehashes of UNIX, which is almost 40 years old.

    Neither are Flight Simulators or other games, most of the classes of computer gaming available today were available in 1980 (they weren't as pretty, but the concepts were there). MMORPG's have their origins in the MUDs of the late 1970's and early 1980's.

    The bottom line is that defining innovation as strictly "Doing something that nobody has ever done before" is so restrictive that it's pointless. Instead, focus on what's new and different. That's where the TRUE innovation lies.

    As a simple example, as Robert mentioned in one of his comments, Microsoft Office was innovative. Each of the applications bundled in office wasn't new, but nobody had ever thought of creating a suite by packaging other products together.  And Office wasn't just innovative marketing. Up until the debut of Office, developers created suites like Lotus Symphony, Claris Works, etc...You bundled a spreadsheet, word processor, and database into a single application, which did none of the features as well as the stand-alone applications.

    The thing that made Office successful (and unbelievably innovative) was the incredible amount of synergy you got from the different applications - you could take a spreadsheet, drop a chart created from the spreadsheet into a word document, and have the word document be updated in real-time as the spreadsheet changed. For the PC industry, THAT was innovative, and 100% technical. And EACH of the applications was as full featured as the others - so you didn't sacrifice functionality for coolness.

    The thing is that innovation isn't always being the first with an idea.  Sometimes it's the little things that make a huge difference.

    The innovations in Windows NT are extraordinary (they really are).  Even the much maligned registry is innovative - before the registry existed, every app and its cousin had its own set of configuration files, each of which had its own syntax and standards.  This meant that it was extraordinarily difficult to write management tools to operate on disparate applications, which limited the ability of IT departments to manage diverse configurations.  In addition, there was no real story for per-user configuration in Windows; each application had to solve it in their own way (which usually meant that they didn't bother).  The registry provided a one-stop-shopping solution for a huge set of configuration issues.  This isn't to say that the registry couldn't be abused, it could.  But it was far better than the other solutions (when was the last time you lost a configuration change due to the filesystem not transacting writes to your config data - this was a common problem in pre-registry times).

    NT's ACL infrastructure was just another ACL infrastructure.  But it allowed for a staggering amount of flexibility in implementation - you can do things with the NT ACL infrastructure that are way beyond the concepts that the original creators of the ACL infrastructure intended.

    Robert's original post was a "letter to Bill" about upcoming innovations in content creation.  IMHO, Robert's 100% right-on - with the creation of blogging (and other similar forms of EASY content authoring like moblogging and audioblogging, and podcasting, etc), there is another wave of web content coming, and it's important for MS to be ahead of the curve and not catching up.

    When the web first debuted, everyone talked about how the web provided a level playing field for content creation.  And in many ways that was the case,  But the reality is that barrier to entry for publishing content on the web was way too high - you had to find hosting space, then author pages individually.

    This has affected me personally I've had hundreds of stories and dozens of articles pent up inside me for years now that I couldn't publish because the barrier to entry was too high.  Publishing by hand had too much overhead for me, I didn't want to take the time to design and implement the content management system to handle all the publishing aspects.  And then there was the issue of driving traffic to the site - there's no point in putting up content if people aren't going to read it.  Until services like Blogger.com etc came along, there wasn't an easy mechanism for updating content and informing consumers that the content had changed. Blogging/RSS/whatever gives all of these, which means that the complexity level required to produce content has been lowered again. 

    But blogging and its friends have lowered the bar to the point where Abby could easily start producing content.  The barriers to entry are low enough that ANYONE can start a blog, and the creation of content has been democratized.  Creation of content is now trivial, and that's a good thing.

     

  • Larry Osterman's WebLog

    Larry's rules of software engineering, Part 3: "Zero Defects" doesn't result in zero defects

    • 24 Comments

    I've held off on writing this particular post for a while, since it's somewhat controversial, but what the heck, you only live once :).

    As Fred Brooks pointed out in his seminal "The Mythical Man Month" (a title that EVERY engineer should have on their shelf), one of the unavoidable aspects of software engineering is the bug curve.  As Brooks explained it, the bug count associated with every software project has a rather distinctive curve.

    At the start of the project, the number of bugs is quite small, as developers write new code.  Since the new code has significant numbers of bugs, the bug count associated with that new code increases in proportion to the number of lines of new code written.

    Over the course of time, the number of bugs found in the code increases dramatically as test starts finding the bugs in the new code.  Eventually, development finally finishes writing new code and starts to address the bugs that were introduced with the new code, so the rate of bug increase starts to diminish. At some point, development finally catches up with the bug backlog and starts to overtake with the new bugs being discovered by test.

    And that's when the drive to ZBB (Zero Bug Bounce) finally begins, indicating that the project is finally on the track to completion.

    Over time, various managers have looked at this bug trend and realized that they can stop this trend of developers introducing new bugs, test finding them, and so on by simply mandating that developers can't have any active bugs in the database before writing new code.  The theory is that if the developers have to address their bugs early, there won't be a bug backlog to manage, and thus there will be a significant reduction in the time to ZBB.  This means that the overall development time for the project will be reduced, which means that the cost of development is lower, the time-to-market is sooner, and everyone is happy.

    On paper, this looks REALLY good.  Developers can't start working on new features until they have addressed all the bugs in the previous features, this means that they won't have to worry about a huge backlog of bugs when they start on the new feature.  Forcing development to deal with their bugs earlier means that they'll have a stronger incentive to not introduce as many bugs (since outstanding bugs keep the developers from the "fun stuff" - writing new code).

    The problem is that Zero Defects as often promoted doesn't work in practice, especially on large projects.  Fundamentally, the problem is that forcing developers to keep their bug slate clean means that developers can be perpetually prevented from writing new code.

    This is especially true if you're dealing with a component with a significant history - some components have intractable bugs that would require significant rewrites of the code to resolve, but the rewrite that is necessary to fix the bug would potentially destabilize hundreds (or thousands) of applications. This means that the fix may be worse than the actual bug.  Now those bugs are very real bugs, so they shouldn't be ignored (and thus the bugs shouldn't be resolved "won't fix"), on the other hand, it's not clear that these bugs should stop new development - after all, some of them may have been in the component for two or three releases already.

    Now this isn't to say that they shouldn't be fixed eventually.  They absolutely should be, but there are always trade-offs that have to be made.  Chris Pratley (who need to blog more :)), over on the Office team has a wonderful blog post about some of the reasons that Microsoft decides not to take bug fixes, before anyone criticizes my logic in the previous paragraphs ("But of course you need to fix all the bugs in the product, stupid!"), they should read his post.

    But the thing is that these bugs prevent new development from proceeding.  They're real bugs, but they're not likely to be fixed, and it may take months to determine the correct fix (which often turns them into new work items).  The other problem that shows up in older code-bases is bug churn.  For some very old code-bases, especially the ones written in the 1980's, there is a constant non zero incoming bug rate.  They show up at a rate of one or two a month, which is enough to keep a developer from ever starting work on new features.

    In practice, teams that attempt to use ZD as a consistent methodology to reduce development time on large scale projects have invariably found that it doesn't reduce the overall development time.

    If, on the other hand, you apply some rational modifications of ZD, you can use the ZD concepts to maintain consistent code quality throughout your project.  For instance, instead of mandating an absolute zero defects across developers, set criteria about the bugs that must be fixed.  For example, bugs that are older than 2 years may be lower priority than new bugs, security bugs (or potential security bugs) are higher priority than other bugs, etc.

    Also, instead of setting an absolute "zero" defects policy, instead, set a limit to the number of bugs that each developer can have outstanding - a this adds some flexibility in dealing with the legacy bugs that really must be fixed, but shouldn't preclude new development.  Also, as this Gamasutra article indicates, it's often useful to have a "ZD push" where the entire team drives to get their bug backlog down.

    In general, I personally think that ZD has a lot in common with a large part of the XP methodology - it works fine in small test groups, but doesn't scale to large projects.  On the other hand, I think that at least one aspect of XP - TDD has the potential to completely revolutionize the way that mainstream software engineering is done.  And I'll write more about that later.

    Edit: Darned newsgator/word/oulook/.text formatting issue.

     

  • Larry Osterman's WebLog

    Fuzzy interfaces

    • 21 Comments

    Mschaef took my challenge up, and submitted the Michal Zalewski article to Slashdot the other day, and the editors at Slashdot decided to run it.

    I found the reactions on Slashdot (and my blog) to be rather fascinating.

    First off, I'm not the person who did the research.  That was Michal, and his work was (as usual) quite good.  I'm flattered that some people thought that I was something more than the messenger, but...

    The first poster on Slashdot, Eponymous Cowboy, got it totally right, and I'm really happy about that.  His comment was (paraphrased):

    Since it may not be obvious to all readers, be aware that when you can make a program crash by feeding it bad data, you can typically further manipulate the data you are sending it to take control of the program. That means a security hole. This is how buffer-overruns work. You can't always do it, but you can think of each way you can crash a program as a little "crack" in its exterior. If you can figure out a way to pry apart the crack, you've got yourself a hole.

    He's totally right.  And the number of people who seem to believe that it's better to crash than to fail on invalid input distresses me immensely. 

    Some of the Slashdot comments indicated that Michal's test was somehow "unfair" because it tested things like null characters in the middle of strings.  The thing is, the bad guys are going to try that too.  And if your browser can't deal with it, then...

    If we lived in a world where the bad guys were forced to write valid code, then that attitude would probably be ok, but that's not the world in which we live.  The bad guys aren't going to use valid HTML to attack your web browser.  They're going to use invalid HTML.  So it's critical that your browser deal with both valid AND invalid HTML.

    Your browser doesn't always have to render the invalid HTML, but it needs to deal with it in SOME reasonable manner.  If it drops the connection and throws up a "This is not strict HTML, I won't render it" dialog box upon seeing its first invalid sequence, that's ok (it may not be a useful customer experience, but that's a different issue).  But it can't crash.

    If it crashes, then you've just let the bad guys own your customers machine.  At a minimum, they're going to be able to launch a DoS attack against your customers.  At the worst, they're going to own your customers machines.

    One of the basic tests that MUST be performed on any exposed interface is what is known as "fuzzing".  Michael Howard and David LeBlanc wrote a about it in their "Writing Secure Code", but essentially fuzzing an interface means taking the interface definition and varying the input.

    So if I wanted to fuzz HTML, I'd start by taking legal HTML, but start injecting invalid characters into the input - I'd try null characters, characters outside the ASCII (0-127) range, illegal UTF8 characters.  I'd then try arguments to ATOMs that were really long (longer than 64K, for example).  I'd try eliminating trailing tags, or nesting tags really, really deeply.  Etc.

    The interesting thing about fuzzing an interface is that it can be automated, as Michal pointed out.

    The other thing about fuzzing is that it should be a basic part of any security testers arsenal.  Your code can't be considered to be secure unless its external interfaces have been fuzzed.

     

     

     

  • Larry Osterman's WebLog

    IE Code quality commentary...

    • 47 Comments

    I just saw this post by Michal Zalewski on BugTraq.  From the post:

    It appears that the overall quality of code, and more importantly, the
    amount of QA, on various browsers touted as "secure", is not up to par
    with MSIE; the type of a test I performed requires no human interaction
    and involves nearly no effort. Only MSIE appears to be able to
    consistently handle [*] malformed input well, suggesting this is the
    only program that underwent rudimentary security QA testing with a
    similar fuzz utility.

    I'm wondering when Michael's post will show up on slashdot.

    Edit: Corrected Michal's name - Sorry about that.

     

  • Larry Osterman's WebLog

    So how do I play sounds anyway?

    • 11 Comments

    Well, the answer to that is WAY more complicated than I can answer, there are LOTs of different ways, and I’m not qualified to talk about most of them.

    But I can talk about some of the easier ways to make noises.

    The first, and simplest one is PlaySound (and its little brother sndPlaySound).

    The PlaySound API takes a WAV file (or resource) and plays its contents.  A WAV file is essentially raw PCM data with additional information describing the sample size and the sample rate (there’s more to it, but that works).

    So to play a “ding”, you can just call:

            PlaySound(“ding.wav”, NULL, SND_FILENAME);

    That’s it, the “ding” sound will play on your speakers.

    But that’s not all you can do with PlaySound.  You can embed a resource in your executable and play that resource by specifying SND_RESOURCE – in that case, the sound name (ding.wav in the example above) is a resource ID and that resource will be extracted and treated as a WAV file and played.

    In addition, you can use PlaySound to play sounds that are controlled by the sound themes control panel applet.  All versions of Windows support a set of default events (SystemAsterisk, SystemExit, SystemStart, etc) that can be played by simply calling:

            PlaySound(“SystemAsterisk”, NULL, SND_ALIAS);

    This will play whatever sound is set for the “Asterisk”.

    PlaySound can also be used to play sounds looped (with SND_LOOP), you can have it play asynchronously (SND_ASYNC), etc.

    One important caveat with SND_ASYNC – SND_ASYNC means that the playback of the sound file is asynchronous.  But before the sound can be played, the sound needs to be loaded into memory, which involves loading resources, opening files, etc, and all of these can take some time to complete.

    If all you need to do for your app is to make dings and whatnot, PlaySound is actually pretty cool, and remarkably lightweight.

     

  • Larry Osterman's WebLog

    Canons

    • 21 Comments

    I recently received an email message with the following: "....involves the canonicalization (it's a word. I swear.) of ..."

    I went through a long period of discussion with a tester once about my use of that word, he also insisted that it wasn't a word (and Microsoft Word's dictionary doesn't believe it's a word (it thinks it should be cannibalization)).

    But canonicalization is a totally reasonable word.‚  It describes the process of turning data into canonical form.

    The next question that the tester asked me was "what on earth does that word mean?"

    Actually Canonical is one of my favorite words, because of its etymology.

    Canonical means, roughly "the word of G_d"‚  Yup, that's right‚  You have to go back to the original derivation of the root word "canon" to realize that.

    According to dictionary.com, "canon" means "An ecclesiastical law or code of laws established by a church council".

    And canonical means roughly "Of, relating to, or required by canon law". It also means "Conforming to orthodox or well-established rules or patterns, as of procedure", but to my interpretation, that alternate definition flows from the first.  Since canon law is the law of the church, and the church's authority flows from god, being canonical would by inference become "conforming to orthodox or well established rules"‚  After all, what is more "orthodox or well established" than the word of G.d?

    And now, working the derivation forward, we run into a word that dictionary.com doesn't recognize: canonicalize.‚  Fortunately, bartleby.com comes to the rescue, with its definition of the "ize" suffix: Bartleby says that it is used in English to turn an adjective into a verb.  So canonicalize is the verb form of canonical. That makes sense.

    And similarly, canonicalization is the process of canonicalizing.

    So Canonicalization is the process of making something conform to the word of G_d.

    Something to think about when writing canonical ACLs or canonicalizing your URLs :).

     

     

  • Larry Osterman's WebLog

    Can my STA object create worker threads?

    • 9 Comments

    For some reason, a bunch of COM related stuff's been coming onto my radar lately, so for some reason, I've been dealing with a lot of COM related issues.  Go figure this one out.

    The other day, I received a message from a co-worker asking me (roughly) "I have an apartment threaded COM component.  Does this mean that I can't use threads in the object?"

    It's a really good question, and I had to think about it for a while before answering.

    CAVEAT: I'm not a COM person, so it's possible the answer is more complicated than this, but here goes.

    The simple answer is that by declaring your component as being apartment threading is an indicator to the CREATOR of your object that it's only safe to call the methods on your object from the thread that initially created the object.  The contract says NOTHING about how you internally implement your component.  So you are free to use whatever internal mechanisms you want in your component.

    However, as always, there is a huge caveat.

    By declaring your COM object as apartment threaded, you've told COM that the methods on the interfaces to your object can only be called from the thread that created the object.  That means that if COM ever drops one of the standard marshallers between your other threads and the interfaces (which shouldn't happen, but might in some COM interop scenarios, or if you're subclassing the standard marshaller, or if your COM object is an inproc handler), then COM's going to enforce the threading model you declared for your object.  Which means that COM might start returning RPC_E_WRONGTHREAD to your method calls if they go through the marshaller. 

    The thing is that you might get away with this for years (since none of the COM marshallers will be involved in most in-proc scenarios), until some point in the future when your component is called in a scenario you don't expect and all of a sudden, the COM marshaller gets in the way and all of a sudden things stop working.

     

  • Larry Osterman's WebLog

    Fascinating article on Passphrases just posted

    • 11 Comments

    I just ran across a totally fascinating article by Jesper Johansson about the use of passphrases instead of passwords.  I switched to using passphrases after reading Robert Hensing’s blog post from July, and I’ve not gone back.  Robert’s post recently showed up rather prominently on FullDisclosure, so Jesper’s article is rather timely (btw, Jesper’s article is the second of three, the first can be found here)

    Jesper takes a far more formal look at the concept of using a passphrase instead of a password, and comes to the somewhat surprising conclusion that a passphrase isn’t necessarily more secure than a password.  They can be more secure, but according to Jesper, a 5-6 word passphrase is just about as strong as a 9 character password.

    Either way, it’s a fascinating article.

     

  • Larry Osterman's WebLog

    What's wrong with this code, part 7

    • 22 Comments

    This rather fascinating issue came up in an internal DL on Friday.  Consider the following hypothetical code that return TRUE if a particular class is registered with COM:

    bool IsClassRegistered(CLSID ClassID)
    {
        HKEY classesKey = NULL;
        HKEY clsidKey = NULL;
        LONG result;
        LPOLESTR classString = NULL;
        bool returnVal = false;
        result = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID", 0, KEY_READ, &classesKey);
        if (result == NO_ERROR)
        {
             HRESULT hr;
             hr = StringFromCLSID(ClassID, &classString);
         
        if (hr != S_OK)
             {
         
            goto Cleanup;
             }
             result = RegOpenKeyExW(classesKey, classString, 0, KEY_READ, &clsidKey);
         
        if (result == NO_ERROR)
             {
                 returnVal =
    true;
             }
        }
    Cleanup:
       
    if (classString != NULL)
        {
            CoTaskMemFree(classString);
        }
       
    if (clsidKey != NULL)
        {
            RegCloseKey(clsidKey);
        }
        
    if (classesKey != NULL)
        {
            RegCloseKey(classesKey);
        }
        
    return returnVal;
    }
     

    In this case, there are two known issues, one serious, one that is somewhat more hypothetical.  In order to understand both of these issues, it's necessary to understand the context for this code.  In this case, a service author decided that they wanted to be clever about their COM registration, and delayed their COM registration until the first user API called.  They wrote this function to see if they had already registered their class.

    As always, answers tomorrow, with associated kudos and mea culpas :)

     

  • Larry Osterman's WebLog

    The crazy things they're using computer games for these days.

    • 14 Comments

    Valorie pointed me to this article at the Washington Post (Free registration required, sorry :(). 

    Everyone knows about the current crop video games - Doom 3, Halo 2, Half-Life 2, The Sims 2, GTA: Vice City, etc.  Even the U.S. Army's gotten on the bandwagon with their quasi-recruiting games (Full Spectrum Warrior and America's Army).

    But then there are some of the more obscure games that will be hitting the market during the next year...

    For instance, there's Glucoboy, a game for the Gameboy to help kids with Juvenile Diabetes, and SuperCharged! which helps kids understand physics.

    And they're releasing the Federal Budget Game, a game to solve the federal budget deficit.

    There's even a trade group for people who write games about serious subjects, the "Serious Games Initiative", and they have a trade show that ran on Monday and Tuesday of this week, the "Serious Games Summit".  This is a big deal forum, sponsored by the U.S. Army, the GDC, Gamasutra, etc.

    My only issue with this is, I thought games were supposed to be for fun, not serious...

    On the other hand, with panels like "Non Combat Military Game Efforts", "Is Open Source the Silver Bullet for Costs, Time and Process", "The Potential of Games in Healthcare" and "Things You Should Know About Serious Games But Probably Don't: Better Collaboration by Avoiding Key Stumbling Blocks", how can they go wrong?

     

  • Larry Osterman's WebLog

    Really cool new app just released

    • 13 Comments

    I just learned that the PhotoStory 3 team finally released their app today.

    I’ve seen demos of this puppy, and it is pretty darned amazing.

    Basically it takes your photos and turns them into a WMV video slideshow. 

    Scoble’s promised to have a post up on PS3 on Channel9 by 3PM today.

    I can’t wait to download it, I’ve been asked to do a slideshow of all the photos that Valorie took at Daniel’s soccer games over the year, this looks to be perfect for it.

     

  • Larry Osterman's WebLog

    Awesome article on how a hacker can compromise a network

    • 14 Comments

    Jesper Johansson just posted an article in TechNet magazine describing how a hacker might take over your network.

    One tidbit from the conclusions:

    Once a network has been thoroughly hacked, the system administrator has three options: update their resume, hope the hacker does a good job running the network, or drain the network. You will of course need to take action to deal with the attack. Let's first take a look at some of the available options and assumptions and consider why they might not be the best course of action when cleaning a hacked system.

     While his article shows how a single vulnerability can be exploited to totally own a network, his conclusion is totally relevant:

    In this article, I've examined how a Windows-based network might be hacked. I hasten to point out that Windows-based networks are no less secure than any other network. While the specific attacks used in this article are unique to Windows, minor modifications to the techniques and a new tool set would make the same compromise possible on a network running a different platform. The problem is not the platform itself, but the practices. All platforms are securable, but all networks are exploitable if they are not architected and implemented carefully. Poor implementation is always poor implementation, regardless of the underlying platform.

    This isn’t a “windows” problem.  It’s a secure system problem.  Once the vulnerability is exploited, the ONLY difference between compromising a Windows system and a *nix system (or an OSX system, or any other system) is the tools that are used to compromise the system.

     

  • Larry Osterman's WebLog

    Wow, Jim Horne's blogging too...

    • 2 Comments

    Darned, Everyone’s getting into the act.  I’ve known Jim for a really long time (back in the Lan Manager days, it’s cool to see he’s publishing (non technical, but…).  His blog can be found here.

     

    Found via Danny Glasser’s blog.

     

  • Larry Osterman's WebLog

    Fall Foliage

    • 15 Comments

    As I mentioned last week, I spent last weekend in upstate New York.  As a part of that, I drove from western Massachusetts to NYC down the Taconic Parkway (possibly the most beautiful piece of highway in the northeast).  I had an opportunity to drive around the town I grew up in (Briarcliff Manor) and generally enjoyed myself.  The fall foliage was almost in full color, and as I drove, I was reminded just how beautiful it is.

    Here in the Pacific Northwest, there are too many evergreen trees to make our fall foliage display nearly as spectacular as it was in the Northeast, but we DO have our own brand of fall foliage that shows up every year.

    You see, here in the Northwest, we have our own fall foliage season.  It starts about mid-August, and continues until it's harvest time on November Third (although some of the foliage isn't harvested until the following year).

    The Northwest fall foliage appears mostly on the median strip on just about every road in the Seattle area, although it often appears peoples yards and other visible spaces.  In more densely trafficked areas, it's more obvious, but as the season progresses, it's pretty much everywhere.

    Of course, I'm talking about political yard signs.

    There's a long history of politicians (and their supporters) trying to see which campaign can put out the most beautiful yard signs display in a particular median.  Sometimes you find eight or nine signs for the same candidate in a single median strip (sort-of like planting perennials).  It's also fascinating watching the "competition" for space that goes on - supporters of one candidate seem to always manage to put their signs directly in front of the signs of their opponent.

    It's also fascinating looking what groups put the yard signs out.

    For some reason, Republicans seem to put out orders of magnitude more yard signs than Democrats.  I'm assuming that the Democrats don't believe that they're effective, while the Republicans do.  I don't know if this is because my area of the state tends to vote Democratic and thus the Republicans feel they need to increase their visibility, or what, but it's a clear trend.

    I've also had an interesting time looking closely at the signs, especially when I'm stopped at a traffic light.  If you look at a yard sign closely, you can often see a "union bug" in the margin.  A union print shop will usually add this relatively unobtrusive mark to the sign that indicates that it was printed by a union shop.  I first noticed the bug when I tried to figure out what the blotch in the white border of a sign I saw in a median, now I look for it every time I see a sign.  If you go here you can see the union bug in some of the signs, for example, it's the little white blotch at the bottom right of the "Re-Elect Jack Markell" sign.

    For some reason, Democratic signs tend to have union bugs present, while Republican signs don't.  This isn't a reliable metric, many Republican signs do have union bugs, and many Democratic signs don't, but again, the trend is pretty clear.  It's not also clear that all signs produced by a union shop have union bugs on them - none of these signs seem to have bugs on them, but the company that produces them is a union shop.

    This year, there's been an unfortunate trend of yard sign vandalism, there are four or five huge yard signs on I-405 Southbound just north of Canyon Park here that were totally trashed (the vandals trashed both the Democratic and the Republican signs there).  I personally find this deplorable - just because you disagree with someone's politics doesn't mean you have the right to destroy their property - and those big signs are EXPENSIVE.

    Btw, for aspiring candidates, while I was writing this up, I ran across this primer on how to use yard signs to their greatest effect :).

     

  • Larry Osterman's WebLog

    What's wrong with this code, part 7: The answers

    • 3 Comments

    To fix the newsgator issues, I switched to using blogjet to post to my blog.  Unfortunately, apparently there are some issues with my workflow that need to be resolved, this was posted yesterday, but didn't get noticed on most peoples aggregators (I've not yet seen the post in newsgator, for example).  So I'm reposting it to let people pick it up as a new post.  I'm still trying to figure out how to get past the double encoding problem...  Sorry about the duplicate post :(

    As I mentioned in yesterday's post, there are two intentional bugs in the code.

    The first bug is a huge one.  Remember my comment about the context of the API: "a service author decided that...".  The problem is that the HKEY_CLASSES_ROOT predefined registry key they used to open the classes database cannot be used in a service (or in an application that impersonates users). 

    The problem is that the HKEY_CLASSES_ROOT key isn't a real registry key - it contains registry entries from both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER.  To retrieve the values from HKEY_CURRENT_USER, advapi32 opens this key under the covers and caches the result until the process is terminated.

    And opening HKEY_CURRENT_USER is a big no-no when you're running as a service.  The problem is that the registry handle for HKEY_CURRENT_USER (or any of the user hives) keeps a reference to the users token.  For the NT authentication scheme this extra token isn't horrendously bad (although it WILL cause major problems with romaing profiles, and thus should be avoided at all costs).  But there are other authentication mechanisms available for Windows that require that a user only be logged onto a single machine at a time.  This means that this token handle that's being kept open by your service prevents that user from logging onto ANY machine on the network, and will continue to prevent the user from logging on until your service terminates.

    To fix this problem, as the documentation for HKEY_CLASSES_ROOT states, you should NEVER use HKEY_CLASSES_ROOT from a service (or from an interactive application that impersonates users other than the logged on user).  Instead, you should use RegOpenUserClassesRoot to open the per-user class database, and HKEY_LOCAL_MACHINE\Software\Classes when opening the per-machine class database.

    Btw, all of the COM APIs know about this restriction, and they correctly respect the HKLM vs HKCU split.  So if you're using the documented COM APIs, you won't ever have a problem.

    And that leads to the second bug in the code.  The second bug is somewhat more hypothetical, but no more significant.  Essentially, the structure of the data under HKEY_CLASSES_ROOT is considered to be internal-only.  The COM registry entries are fully documented to allow a user to add a COM component, but they do not guarantee that you can determine which DLL is going to be loaded by following the rules laid out in my "How does COM activation work anyway" post.  If a COM object is distributed as a side-by-side assembly then the rather simplistic rules I wrote about in my "How does COM activation work anyway?" post for InprocServer32 activation don't completely describe the process by which the DLL is loaded.

    If you use CoCreateInstance, then the right thing will happen, but if you attempt to assume that you know what COM is going to do to activate your code, then at some point in the future, you're likely to discover that you got it wrong and have to hotfix your code to resolve the issue.

    So there are two takeaways for this "What's wrong with this code".  The first is "Don't use HKCR unless you KNOW that your code will NEVER be invoked in a service".  The second one is "The COM APIs are your friend, trust COM, it knows what it's doing, do not presume that you can do a better job than it does, for therein lie dragons".

    Now for kudos and mea culpas:

    I'm not surprised nobody found the second bug - it was an incredibly subtle bug, and not at all clear.

    Once again, Mike Dimmick nailed the first bug.  In addition, he pointed out one of the major problems with HRESULTs - people use SUCCEDED to check for success, when S_FALSE is a success return code that means "I didn't do what you wanted me to, but it wasn't your fault" - S_FALSE is effectively the same as a failure lurking under the success class of return values.

    Paul Winwood pointed out that I should have used a tighter restriction than KEY_READ in my security flags.

    Peter da Silva and Mo pointed out that if the code was used in its stated purpose that there was a race condition that would cause registry entries to be doubly written.

    Mike R asked if CoTaskMemFree should be SysFreeString.  The answer is NO, since StringFromCLSID returns a LPWSTR not a BSTR - SysFreeString can only be called on a BSTR - they're different (see this most excellent post by Eric Lippert for details).

    Simon Cooke pointed out that I could safe a registry round trip.  He also brought up issues with Win9x (which is usually outside the scope).

    Chui Tey pointed out that RegOpenKeyEx might change the value of its out values in the failure case.  That's possible, but HIGHLY unlikely, simply because the possibility of breaking applications. 

    Pavel Lebedinsky pointed out (correctly) that if the service isn't impersonating anyone that using HKCR is safe.  This is true on its face, but it needs to be made ABSOLUTELY clear.  There's no enforcement of this, so...  And given the potential of people misusing HKCR, it's safer to just ignore it.

    Edit: I think this didn't show up on peoples aggregators, so...

  • Larry Osterman's WebLog

    Cool Speaker: Ben Mikaelsen

    • 0 Comments

    Last night, I was privileged to be able to go to my son's school and listen to Ben Mikaelsen speak.  Ben's an author of children's books (really YA, but they're incredibly sophisticated for YA books), and he lives in Montana with his companion Buffy - an 800 pound bear.

    Ben's an absolutely amazing speaker, he spoke on his childhood, the people who affected him the most as a kid, and his other life experiences working as a writer.  He grew up in Guatamala, during the revolutions (he talked about using sticks to poke the dead bodies on the street)), .  Eventually he moved to the US as a functionally illiterate 7th grader, where he was turned onto reading by a librararian.

    Ben's had an amazing life.  I loved hearing the backstory behind some of his books.

    It was absolutely wild listening to him recount his experiences in Space Camp (doing research for his book Countdown).  The story of how he met the only surviving German scientist of the 500 who came from Germany with Werner von Braun (unfortunately, I missed the scientist's name) was amazing.  To paraphrase one part: You know how, when you go to visit people, they take out their photo albums, and show you all the pictures of their trip to Disneyland?  "Look - here's me in front of Space Mountain" "Look, here's me with Mickey!" "Look, here's me with Goofy!".  Well this guy opened up his snapshot album and took out the pictures.  "Look, here's me with a V2 rocket.   I worked on the avionics for that one.  If we'd had just a couple more months to work on it, we could have changed the world" "Look, here's me with my fuhrer - he did so many bad things, I'll never sleep at night because of the thigns he did" "Look, here's me with president Kennedy.  Such a shame, he died too early".

    I am so utterly envious of his experiences, I wish I had done half of the things he talked about doing.

    If you EVER have an opportunity to hear him speak, go to listen, you won't regret it.

     

  • Larry Osterman's WebLog

    Sharron absolutely Rocks!

    • 2 Comments

    Sharron and Oliver (and the rest of the family) spent Sunday at the Whidbey Equestrian Center, Sharron was participating in the Whidbey Equestrian Center’s Fall Dressage Schooling show.  I previously blogged about her first competition back in March.

    This time, she competed in the Intro level B and the Training Level 1 test.  She took first place in the Training Level test (with a 65%), and third place in the Intro test (with a 62%).  As I mentioned in the previous post, these are extremely competitive scores, especially for a ten year old rider.

    No pictures yet, but hopefully I’ll have something soon.

    Way cool!

     

  • Larry Osterman's WebLog

    Ok, so I lied (a bit)

    • 2 Comments

    Last week I posted that work was getting busy and the blog would likely go dark.

    Well, that was a bit of a white lie.  In fact, the blog went dark because the kids and I went east to attend a surprise birthday party for my Father's 70th birthday.

    Unfortunately, since he sometimes reads my blog, I had to lie to keep the surprise :).

    It was a lot of fun (if very short), most of the family was there, it was great seeing everyone again.

    Anyway, sorry for that, I'm back now (and wading through the 400 email messages in my inbox). Hopefully I'll get out from under it and get more technical content out for tomorrow.

     

  • Larry Osterman's WebLog

    What's wrong with this code, Part 7: The answers.

    • 4 Comments

    As I mentioned in yesterday's post, there are two intentional bugs in the code.

    The first bug is a huge one.  Remember my comment about the context of the API: "a service author decided that...".  The problem is that the HKEY_CLASSES_ROOT predefined registry key they used to open the classes database cannot be used in a service (or in an application that impersonates users). 

    The problem is that the HKEY_CLASSES_ROOT key isn't a real registry key - it contains registry entries from both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER.  To retrieve the values from HKEY_CURRENT_USER, advapi32 opens this key under the covers and caches the result until the process is terminated.

    And opening HKEY_CURRENT_USER is a big no-no when you're running as a service.  The problem is that the registry handle for HKEY_CURRENT_USER (or any of the user hives) keeps a reference to the users token.  For the NT authentication scheme this extra token isn't horrendously bad (although it WILL cause major problems with romaing profiles, and thus should be avoided at all costs).  But there are other authentication mechanisms available for Windows that require that a user only be logged onto a single machine at a time.  This means that this token handle that's being kept open by your service prevents that user from logging onto ANY machine on the network, and will continue to prevent the user from logging on until your service terminates.

    To fix this problem, as the documentation for HKEY_CLASSES_ROOT states, you should NEVER use HKEY_CLASSES_ROOT from a service (or from an interactive application that impersonates users other than the logged on user).  Instead, you should use RegOpenUserClassesRoot to open the per-user class database, and HKEY_LOCAL_MACHINE\Software\Classes when opening the per-machine class database.

    Btw, all of the COM APIs know about this restriction, and they correctly respect the HKLM vs HKCU split.  So if you're using the documented COM APIs, you won't ever have a problem.

    And that leads to the second bug in the code.  The second bug is somewhat more hypothetical, but no more significant.  Essentially, the structure of the data under HKEY_CLASSES_ROOT is considered to be internal-only.  The COM registry entries are fully documented to allow a user to add a COM component, but they do not guarantee that you can determine which DLL is going to be loaded by following the rules laid out in my "How does COM activation work anyway" post.  If a COM object is distributed as a side-by-side assembly then the rather simplistic rules I wrote about in my "How does COM activation work anyway?" post for InprocServer32 activation don't completely describe the process by which the DLL is loaded.

    If you use CoCreateInstance, then the right thing will happen, but if you attempt to assume that you know what COM is going to do to activate your code, then at some point in the future, you're likely to discover that you got it wrong and have to hotfix your code to resolve the issue.

    So there are two takeaways for this "What's wrong with this code".  The first is "Don't use HKCR unless you KNOW that your code will NEVER be invoked in a service".  The second one is "The COM APIs are your friend, trust COM, it knows what it's doing, do not presume that you can do a better job than it does, for therein lie dragons".

    Now for kudos and mea culpas:

    I'm not surprised nobody found the second bug - it was an incredibly subtle bug, and not at all clear.

    Once again, Mike Dimmick nailed the first bug.  In addition, he pointed out one of the major problems with HRESULTs - people use SUCCEDED to check for success, when S_FALSE is a success return code that means "I didn't do what you wanted me to, but it wasn't your fault" - S_FALSE is effectively the same as a failure lurking under the success class of return values.

    Paul Winwood pointed out that I should have used a tighter restriction than KEY_READ in my security flags.

    Peter da Silva and Mo pointed out that if the code was used in its stated purpose that there was a race condition that would cause registry entries to be doubly written.

    Mike R asked if CoTaskMemFree should be SysFreeString.  The answer is NO, since StringFromCLSID returns a LPWSTR not a BSTR - SysFreeString can only be called on a BSTR - they're different (see this most excellent post by Eric Lippert for details).

    Simon Cooke pointed out that I could safe a registry round trip.  He also brought up issues with Win9x (which is usually outside the scope).

    Chui Tey pointed out that RegOpenKeyEx might change the value of its out values in the failure case.  That's possible, but HIGHLY unlikely, simply because the possibility of breaking applications. 

    Pavel Lebedinsky pointed out (correctly) that if the service isn't impersonating anyone that using HKCR is safe.  This is true on its face, but it needs to be made ABSOLUTELY clear.  There's no enforcement of this, so...  And given the potential of people misusing HKCR, it's safer to just ignore it.

    Edit: I think this didn't show up on peoples aggregators, so...

Page 1 of 2 (26 items) 12