Larry Osterman's WebLog

Confessions of an Old Fogey
  • Larry Osterman's WebLog

    One in a million is next Tuesday

    • 9 Comments

    Back when I was a wee young lad, fresh from college, I thought I knew everything there was to know.

     

    I’ve since been disabused of that notion, rather painfully.

    One of the best happened very early on, back when I was working on DOS 4.  We ran into some kind of problem (I’ll be honest and say that I don’t remember what it was). 

    I was looking into the bug with Gordon Letwin, the architect for DOS 4.  I looked at the code and commented “Maybe this is what was happening?  But if that were the case, it’d take a one in a million chance for it to happen”.

    Gordon’s response was simply: “In our business, one in a million is next Tuesday”.

    He then went on to comment that at the speeds which modern computers operate (4.77 MHz remember), things happened so quickly that something with a one in a million chance of occurrence is likely to happen in the next day or so.

    I’m not sure I’ve ever received better advice in my career. 

    It has absolutely stood the test of time – no matter how small the chance of something happening, with modern computers and modern operating systems, essentially every possible race condition or deadlock will be found within a reasonable period of time.

    And I’ve seen some absolute doozies in my time – race conditions on MP machines where a non interlocked increment occurred (one variant of Michael Grier’s “i = i + 1” bug).   Data corruptions because you have one non protected access to a data structure.  I’m continually amazed at the NT scheduler’s uncanny ability to context switch my application at just the right time as to expose my data synchronization bug.  Or to show just how I can get my data structures deadlocked in hideous ways.

    So nowadays, whenever anyone comments on how unlikely it is for some event to occur, my answer is simply: “One in a million is next Tuesday”.

    Edit: To fix the spelling of MGrier's name.

    Edit:  My wife pointed out the following and said it belonged with this post: http://www.jumbojoke.com/000036.html

  • Larry Osterman's WebLog

    What's wrong with this code, part 10

    • 64 Comments
    Ok, time for another "what's wrong with this code".  This one's trivial from a code standpoint, but it's tricky...

    // ----------------------------------------------------------------------
    // Function:
    // CThing1::OnSomethingHappening()
    //
    // Description:
    // Called when something happens
    //
    // Return:
    // S_OK if successful
    //
    // ----------------------------------------------------------------------
    HRESULT CThing1::OnSomethingHappening()
    {
        HRESULT hr;
            :

            :
        <Do Some Stuff>
            :
       
        // Perform some operation...
        hr = PerformAnOperation();
        if (FAILED(hr))
            hr = ERROR_NOT_SUPPORTED;
        IF_FAILED_JUMP(hr, Error);

    Exit:
        return hr;

    Error:
        goto Exit;
    }

    Not much code, no?  So what's wrong with it?

    As usual, answers and kudos tomorrow.

  • Larry Osterman's WebLog

    Larry's rules of software engineering #2: Measuring testers by test metrics doesn't.

    • 30 Comments

    This one’s likely to get a bit controversial J.

    There is an unfortunate tendency among test leads to measure the performance of their testers by the number of bugs they report.

    As best as I’ve been able to figure out, the logic works like this:

    Test Manager 1: “Hey, we want to have concrete metrics to help in the performance reviews of our testers.  How can we go about doing that?”
    Test Manager 2: “Well, the best testers are the ones that file the most bugs, right?”
    Test Manager 1: “Hey that makes sense.  We’ll measure the testers by the number of bugs they submit!”
    Test Manager 2: “Hmm.  But the testers could game the system if we do that – they could file dozens of bogus bugs to increase their bug count…”
    Test Manager 1: “You’re right.  How do we prevent that then? – I know, let’s just measure them by the bugs that are resolved “fixed” – the bugs marked “won’t fix”, “by design” or “not reproducible” won’t count against the metric.”
    Test Manager 2: “That sounds like it’ll work, I’ll send the email out to the test team right away.”

    Sounds good, right?  After all, the testers are going to be rated by an absolute value based on the number of real bugs they find – not the bogus ones, but real bugs that require fixes to the product.

    The problem is that this idea falls apart in reality.

    Testers are given a huge incentive to find nit-picking bugs – instead of finding significant bugs in the product, they try to find the bugs that increase their number of outstanding bugs.  And they get very combative with the developers if the developers dare to resolve their bugs as anything other than “fixed”.

    So let’s see how one scenario plays out using a straightforward example:

    My app pops up a dialog box with the following:

     

                Plsae enter you password:  _______________ 

     

    Where the edit control is misaligned with the text.

    Without a review metric, most testers would file a bug with a title of “Multiple errors in password dialog box” which then would call out the spelling error and the alignment error on the edit control.

    They might also file a separate localization bug because there’s not enough room between the prompt and the edit control (separate because it falls under a different bug category).

    But if the tester has their performance review based on the number of bugs they file, they now have an incentive to file as many bugs as possible.  So the one bug morphs into two bugs – one for the spelling error, the other for the misaligned edit control. 

    This version of the problem is a total and complete nit – it’s not significantly more work for me to resolve one bug than it is to resolve two, so it’s not a big deal.

    But what happens when the problem isn’t a real bug – remember – bugs that are resolved “won’t fix” or “by design” don’t count against the metric so that the tester doesn’t flood the bug database with bogus bugs artificially inflating their bug counts. 

    Tester: “When you create a file when logged on as an administrator, the owner field of the security descriptor on the file’s set to BUILTIN\Administrators, not the current user”.
    Me: “Yup, that’s the way it’s supposed to work, so I’m resolving the bug as by design.  This is because NT considers all administrators as idempotent, so when a member of BUILTIN\Administrators creates a file, the owner is set to the group to allow any administrator to change the DACL on the file.”

    Normally the discussion ends here.  But when the tester’s going to have their performance review score based on the number of bugs they submit, they have an incentive to challenge every bug resolution that isn’t “Fixed”.  So the interchange continues:

    Tester: “It’s not by design.  Show me where the specification for your feature says that the owner of a file is set to the BUILTIN\Administrators account”.
    Me: “My spec doesn’t.  This is the way that NT works; it’s a feature of the underlying system.”
    Tester: “Well then I’ll file a bug against your spec since it doesn’t document this.”
    Me: “Hold on – my spec shouldn’t be required to explain all of the intricacies of the security infrastructure of the operating system – if you have a problem, take it up with the NT documentation people”.
    Tester: “No, it’s YOUR problem – your spec is inadequate, fix your specification.  I’ll only accept the “by design” resolution if you can show me the NT specification that describes this behavior.”
    Me: “Sigh.  Ok, file the spec bug and I’ll see what I can do.”

    So I have two choices – either I document all these subtle internal behaviors (and security has a bunch of really subtle internal behaviors, especially relating to ACL inheritance) or I chase down the NT program manager responsible and file bugs against that program manager.  Neither of which gets us closer to shipping the product.  It may make the NT documentation better, but that’s not one of MY review goals.

    In addition, it turns out that the “most bugs filed” metric is often flawed in the first place.  The tester that files the most bugs isn’t necessarily the best tester on the project.  Often times the tester that is the most valuable to the team is the one that goes the extra mile and spends time investigating the underlying causes of bugs and files bugs with detailed information about possible causes of bugs.  But they’re not the most prolific testers because they spend the time to verify that they have a clean reproduction and have good information about what is going wrong.  They spent the time that they would have spent finding nit bugs and instead spent it making sure that the bugs they found were high quality – they found the bugs that would have stopped us from shipping, and not the “the florblybloop isn’t set when I twiddle the frobjet” bugs.

    I’m not saying that metrics are bad.  They’re not.  But basing people’s annual performance reviews on those metrics is a recipe for disaster.

    Somewhat later:  After I wrote the original version of this, a couple of other developers and I discussed it a bit at lunch.  One of them, Alan Ludwig, pointed out that one of the things I missed in my discussion above is that there should be two halves of a performance review:

                MEASUREMENT:          Give me a number that represents the quality of the work that the user is doing.
    And      EVALUATION:               Given the measurement, is the employee doing a good job or a bad job.  In other words, you need to assign a value to the metric – how relevant is the metric to your performance.

    He went on to discuss the fact that any metric is worthless unless it is reevaluated at every time to determine how relevant the metric is – a metric is only as good as its validity.

    One other comment that was made was that absolute bug count metrics cannot be a measure of the worth of a tester.  The tester that spends two weeks and comes up with four buffer overflow errors in my code is likely to be more valuable to my team than the tester that spends the same two weeks and comes up with 20 trivial bugs.  Using the severity field of the bug report was suggested as a metric, but Alan pointed out that this only worked if the severity field actually had significant meaning, and it often doesn’t (it’s often very difficult to determine the relative severity of a bug, and often the setting of the severity field is left to the tester, which has the potential for abuse unless all bugs are externally triaged, which doesn’t always happen).

    By the end of the discussion, we had all agreed that bug counts were an interesting metric, but they couldn’t be the only metric.

    Edit: To remove extra <p> tags :(

  • Larry Osterman's WebLog

    Choosing a C runtime library

    • 28 Comments

    Yesterday a developer in my group came by asking about a failure he saw when running the application verifier on his component.  The app verifier was reporting that he was using a HEAP_NO_SERIALIZE heap from a thread other than the one that created the heap.

    I looked a bit deeper and realized that he was running with the single threaded statically linked C runtime library.  An honest mistake, given that it’s the default version of the C runtime library.

    You see, there are 3 different versions of the C runtime library shipped (and 3 different versions of the ATL and MFC libraries too). 

    The first is the statically linked single-threaded library.  This one can be used only on single threaded applications, and all the object code for the C runtime library functions used is included in the application binary.  You get this with the /ML compiler switch.

    The second is the statically linked, multi-threaded library.  This one’s the same as the first, but you can use it in a multithreaded application.  You get this one with the /MT compiler switch.

    The third is the dynamically linked library.  This one keeps all the C runtime library code in a separate DLL (MSVCRTxx.DLL).  Since the runtime library code’s in a DLL, it also handles multi-threaded issues.   The DLL library is enabled with the /MD switch.

    But I’ve been wondering.  Why on earth would anyone ever choose any option OTHER than multi-threaded DLL version of the runtime library?

    There are LOTS of reasons for always using the multithreaded DLL:

    1)      Your application is smaller because it doesn’t have the C runtime library loaded into it.

    2)      Because of #1, your application will load faster.  The C runtime library is almost certainly in memory, so the pages containing the library don’t have to be read from disk.

    3)      Using the multithreaded DLL future-proofs your application.  If you ever add a second thread to your application (or call into an API that creates multiple threads), you don’t have to remember to change your C runtime library.  And unless you’re running the app verifier regularly, the only way you’ll find out about the problem is if you get a heap corruption (if you’re lucky).

    4)      If your application has multiple DLL’s, then you need to be VERY careful about allocation – each DLL will have its own C runtime library heap, as will the application.  If you allocate a block in one DLL, you must free it in the same DLL.

    5)      If a security bug is ever found in the C runtime library, you don’t have to release an update to your app.

    The last one’s probably the most important IMHO.  Just to be clear - There haven’t been any security holes found in the C runtime library.  But it could happen.  And when it happens, it’s pretty ugly.  A really good example of this can be seen with the security vulnerability that was found in the zlib compression library. This library was shipped in dozens of products, and every single one of them had to be updated.  If you do a google search for “zlib library security vulnerability” you can see some of the chaos that resulted from this disclosure.  If your app used the DLL C runtime library, then you’d get the security fix for free from windows update when Microsoft posted the update.

    The only arguments I’ve been able to come up with for using the static C runtime libraries are:

    1)      I don’t have to distribute two binaries with my application – If I use the DLL, I need to redistribute the DLL.  This makes my application setup more complicated.

    Yes, but not significantly (IMHO).  This page lists the redistribution info for the C runtime library and other components.

    2)      If I statically link to the C runtime library, I avoid DLL hell.

    This is a red herring IMHO.  Ever since VC6, the C runtime library has been tightly versioned, as long as your installer follows the rules for version checking of redistributable files (found here) you should be ok.

    3)      My code is faster since the C runtime library doesn’t have to do all that heap synchronization stuff.

    Is it really?  How much checking is involved in the multithreaded library?  Let’s see.  The multithreaded library puts some stuff that was kept in global variable in thread local storage.  So there’s an extra memory indirection involved on routines like strtok etc.  Also, the single threaded library creates it’s heap with HEAP_NO_SERIALIZE (that’s what led to this entire post J).  But that just wraps the heap access with an EnterCriticalSection/ExitCriticalSection.  Which is very very fast if there’s no contention.  And since this is a single threaded application, by definition there’s no contention for the critical section.

    Using the multithreaded DLL C runtime library is especially important for systems programmers.  First off, if your system component is a DLL, it’s pretty safe to assume that you’ll be called from multiple threads, so at an absolute minimum, you’re going to want to use the multithreaded static C runtime library.  And if you’re using the multithreaded static C runtime library, why NOT use the DLL version?

    If you’re not writing a DLL, then it’s highly likely that your app does (or will) use multiple threads.  Which brings me back to the previous comment – why NOT use the DLL version? 

    You’re app will be smaller, more secure, future-proof, and no slower than if you don’t.

     

  • Larry Osterman's WebLog

    Open Source and Hot Rods

    • 73 Comments

    I was surfing the web the other day and ran into someone linking to this article by Jack Lanier from Edmunds (the automotive newsletter people).

    The article's entitled "Friends Don't Let Friends Modify Cars".

    From the article:

    Today, it's difficult to make cars better and extremely easy to make them worse. Or dangerous.

    As a journalist driving modified cars, I've been sprayed with gasoline, boiling coolant, super-heated transmission fluid and nitrous oxide. (The latter was more entertaining than the former.) Several have burst into flames. Throttles have stuck wide open, brake calipers snapped clean off, suspensions ripped from their mounts and seatbelt mounting hardware has dropped into my lap. All this is on top of the expected thrown connecting rod, blown head gasket, exploded clutch, disintegrated turbocharger and broken timing belt.

    The vast majority of these vehicles were built by professionals. Many were from big-name tuners. Most performed as if they were constructed in a shop class at a high school with a lax drug policy. Once, after a suspension component fell off a car from a big-name tuner, the car actually handled better.

    For every modified and tuner car that performed better than stock, I've driven numerous examples that were slower. If they were quicker, it was often in an area that can't be used on the street. What's the use of gaining 0.2 second in the quarter-mile if the car is slower 0-60 mph? And costs $10,000 more?

    [...]

    Recently, I autocrossed a pair of Subaru WRXs. One was a dead-stock WRX. The other, a tricked-out STi lowered with stiffer springs, shocks and bars and an exhaust kit and air filter. The STi is supposed to have an advantage of some 70 horsepower. Maybe the exhaust and filter moved the power up in the rev band where it couldn't be used. The lowered, stiffened STi regularly bottomed against its bump stops. When a car hits its bump stops, the spring rate goes to infinity and tire grip drops to near zero. This caused the STi to leap into the worst understeer I've experienced with inflated front tires. Meanwhile, in the unmodified WRX, I could be hard in the throttle at the same point. The result: The dead-stock WRX was at least as quick as the STi and far easier to drive. Easy to make worse, harder to make better

    I read this article and was struck by the similarities between this and the open source vs COTS model.

    COTS (Commercial Off The Shelf) software is equivalent to a stock automobile.  They're built by professional engineers, and tested as a whole.  But you don't get to mess with the system.

    On the other hand, open source gives you the ability to join the software equivalent of the tuner/modified market - you can tweak the system to your hearts content.  You may make it go faster, but you're not totally sure what it's going to do to the overall quality of the system.

    In fact, I constantly read that that's one of the huge benefits of open source - on an open source project, if you don't like how something works, you can just step in and fix it, while with COTS you don't have that ability.

    Software engineering is software engineering, whether it's open source or closed source.  Having the ability to tweak code (or an automobile) doesn't automatically mean that the tweak will be higher quality than what it's replacing.  It's entirely possible that it either won't be better, or that the improvement won't really matter.  On the IMAP mailing list, I CONSTANTLY see people submitting patches to the U.W. IMAP server proposing tweaks to fix one thing or another (even though it’s the wrong mailing list, the patches still come in).  And Mark Crispin shoots them down all the time, because the person making the patch didn’t really understand the system – their patch might have fixed their problem and their configuration, but it either opened up a security hole, or broke some other configuration, etc.

    Btw, the same thing holds true for system modifications.  Just because you can put a window into a hard disk doesn’t mean that the hard disk is going to work as well afterwards as it did before you took it apart.

    Just like in the automotive world, simply because you CAN modify something, it doesn't mean that it's a good idea to modify it.

  • Larry Osterman's WebLog

    What’s the difference between GetTickCount and timeGetTime?

    • 26 Comments

    I’ve always believed that the most frequently used multimedia API in winmm.dll was the PlaySound API.  However I recently was working with the results of some static analysis tools that were run on the Windows 7 codebase and I realized that in fact the most commonly used multimedia API (in terms of code breadth) was actually the timeGetTime API.  In fact almost all the multimedia APIs use timeGetTime which was somewhat surprising to me at the time.

    The MSDN article for timeGetTime says that timeGetTime “retrieves the system time, in milliseconds. The system time is the time elapsed since the system started.”.

    But that’s almost exactly what the GetTickCount API returns “the number of milliseconds that have elapsed since the system was started, up to 49.7 days.” (obviously timeGetTime has the same 49.7 day limit since both APIs return 32bit counts of milliseconds).

    So why are all these multimedia APIs using timeGetTime and not GetTickCount since the two APIs apparently return the same value?  I wasn’t sure so I dug in a bit deeper.

    The answer is that they don’t.  You can see this with a tiny program:

    int _tmain(int argc, _TCHAR* argv[])
    {
        int i = 100;
        DWORD lastTick = 0;
        DWORD lastTime = 0;
        while (--i)
        {
            DWORD tick = GetTickCount();
            DWORD time = timeGetTime();
            printf("Tick: %d, Time: %d, dTick: %3d, dTime: %3d\n", tick, time, tick-lastTick, time-lastTime);
            lastTick = tick;
            lastTime = time;
            Sleep(53);
        }
        return 0;
    }

    If you run this program, you’ll notice that the difference between the timeGetTime results is MUCH more stable than the difference between the GetTickCount results (note that the program sleeps for 53ms which usually doesn’t match the native system timer resolution):

    Tick: 175650292, Time: 175650296, dTick:  46, dTime:  54
    Tick: 175650355, Time: 175650351, dTick:  63, dTime:  55
    Tick: 175650417, Time: 175650407, dTick:  62, dTime:  56
    Tick: 175650464, Time: 175650462, dTick:  47, dTime:  55
    Tick: 175650526, Time: 175650517, dTick:  62, dTime:  55
    Tick: 175650573, Time: 175650573, dTick:  47, dTime:  56
    Tick: 175650636, Time: 175650628, dTick:  63, dTime:  55
    Tick: 175650682, Time: 175650683, dTick:  46, dTime:  55
    Tick: 175650745, Time: 175650739, dTick:  63, dTime:  56
    Tick: 175650792, Time: 175650794, dTick:  47, dTime:  55
    Tick: 175650854, Time: 175650850, dTick:  62, dTime:  56

    That’s because GetTickCount is incremented by the clock tick frequency on every clock tick and as such the delta values waver around the actual time (note that the deltas average to 55ms so on average getTickCount returns an accurate result but not with spot measurements) but timeGetTime’s delta is highly predictable.

    It turns out that for isochronous applications (those that depend on clear timing) it is often important to be able to retrieve the current time in a fashion that doesn’t vary, that’s why those applications use timeGetTime to achieve their desired results.

  • 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

    Useful service tricks - Debugging service startup

    • 22 Comments
    For the better part of the past 15 years, I've been working on one or another services for the Windows platform (not always services for windows, but always services ON windows).

    Over that time, I've developed a bag of tricks for working with services, I mentioned one of them here.  Here's another.

    One of the most annoying things to have to debug is a problem that occurs during service startup.  The problem is that you can't attach a debugger to the service until it's started, but if the service is failing during startup, that's hard.

    It's possible to put a Sleep(10000) to cause your service startup to delay for 10 seconds (which gives you time to attach the debugger during start), that usually works, but sometimes service startup failures only happen on boot (for autostart services).

    First off, before you start, you need to have a kernel debugger attached to your computer, and you need the debugging tools for windows (this gets you the command line debuggers).  I'm going to assume the debuggers are installed into "C:\Debuggers", obviously you need to adjust this for your local machine.

    One thing to keep in mind: As far as I know, you need have the kernel debugger hooked up to debug service startup issues (you might be able to use ntsd.exe hooked up for remote debugging but I'm not sure if that will work). 

    This of course begs the next question: "The kernel debugger?  Why on earth do I need a kernel debugger when I'm debugging user mode code?".  You're completely right.  But in this case, you're not actually using the kernel debugger.  Instead, you're running using a user mode debugger (ntsd.exe in my examples) that's running over the serial port using facilities that are enabled by the kernel debugger.  It's not quite the same thing.

    There are multiple reasons for using a debugger that's redirected to a kernel debugger.  First off, if your service is an autostart service, it's highly likely that it starts long before the a user logs on.  So an interactive debugger won't really be able to debug the application.  Secondly, services by default can't interact with the desktop (heck, they often run in a different TS session from the user (this is especially true in Vista, but it's also true on XP with Fast User Switching), so they CAN'T interact with the desktop).  That means that when the debugger attempts to interact with the user, it can't because it flat-out can't because the desktop is sitting in a different TS session.

    There are a couple of variants of this trick, all of which should work.

    Lets start with the simplest:

    If your service runs with a specific binary name, you can use the Image File Execution Options registry key (documented here) to launch your executable under the debugger.  The article linked shows how to launch using Visual Studio, for a service, you want to use the kernel debugger, so instead of using "devenv /debugexe" for the value, use "C:\Debuggers\NTSD.EXE -D", that will redirect the output to the kernel debugger.

     

    Now for a somewhat more complicated version - You can ask the service controller to launch the debugger for you.  This is useful if your service is a shared service, or if it lives in an executable that's used for other purposes (if you use a specific -service command line switch to launch your exe as a service, for example).

    This one's almost easier than the first.

    From the command line, simply type:

    sc config <your service short name> binpath= "c:\debuggers\ntsd.exe -d <path to your service executable> <your service executable options>

     

    Now restart your service and it should pick up the change.

     

    I suspect it's possible to use the ntsd.exe as a host process for remote debugging, I've never done that (I prefer assembly language debugging when I'm using the kernel debugger), so I don't feel comfortable describing how to set it up :(

    Edit: Answered Purplet's question in the comments (answered it in the post because it was something important that I left out of the article).

    Edit2: Thanks Ryan.  s/audiosrv/<your service>/

     

  • Larry Osterman's WebLog

    Private Destructors

    • 17 Comments
    Yesterday, I mentioned that Michael Ruck had complained that I'd made the destructor on my CFooBase class private, and he wondered why on earth I had done it.

    Skywing answered in the comments but it bears a bit more discussion.

    The simple answer to why I made the destructor private was that I didn't want anyone to be able to destroy the object.

    ????

    That's right.  You see, CFooBase is a reference counted objects.  And you NEVER, EVER want to allow someone to delete a reference counted object.

    This is because you don't own the lifetime of the reference counted object.  Really, you don't.  You own the lifetime of your reference to the object, but you have no way of controlling who else is taking a reference to the object.  So the object may live well after you're done with it.

    For example, consider the following case:

    void MyFunction(void)
    {
        CFooBase *myFoo;

        myFoo = new CFooBase();
        <Do Some Stuff>
        delete myFoo;
    }

    Seems pretty straightforward, right?

    Well, no.  The reason is that you have no idea what happened in the <Do Some Stuff> section.  For example, consider:

    void MyFunction(void)
    {
        CFooBase *myFoo;

        myFoo = new CFooBase();
        hr = RegistrationFunction->RegisterForNotifications(myFoo);  // RegistrationFunction takes a reference.
        <Do Some Stuff>
        hr = RegistrationFunction->UnregisterForNotifications(myFoo); // Releases the reference taken earlier
        delete myFoo;
    }

    What's wrong here?  Well, what happens if a notification was being processed during the call to UnregisterForNotifications?  In that case, the notification logic would take ANOTHER reference to the myFoo object (to ensure that the object remains alive during the duration of the callback).  But by deleting the myFoo directly, you're deleting the object out from under the registration function.

    If, on the other hand, you make the destructor for myFoo private, then the call to delete myFoo returns an error, which forces you to rewite the code to look like:

    void MyFunction(void)
    {
        CFooBase *myFoo;

        myFoo = new CFooBase();
        hr = RegistrationFunction->RegisterForNotifications(myFoo);  // RegistrationFunction takes a reference.
        <Do Some Stuff>
        hr = RegistrationFunction->UnregisterForNotifications(myFoo); // Releases the reference taken earlier
        myFoo->Release();    // Remove my reference to the myFoo
    }

    In other words, making the destructor private forces you to use the correct release pattern for refcounted object.

    Of course, the next problem that comes up is the question of deterministic finalism - if the object in question is holding some external resource open and you need to ensure that it's closed its resources.

    Well, the CLR IDisposable pattern comes in quite handy here.  That allows the caller to notify the object that it's done with the object.  Of course, it's also responsible for dealing with the consequences...

    The bottom line is that once you decide to use reference counted objects, you don't control the lifetime of the object, all you do is control the lifetime of a reference to the object.  And declaring the destructor private forces you to recognise this.

  • Larry Osterman's WebLog

    What does style look like, part 4

    • 33 Comments
    Continuing the discussion of "style"..

    Yesterday, I showed the code reformatted into "BSD" style format, which looks like:

    #include "list.h"
    main(C cArg, SZ rgszArg[])
    {
        I iNode;
        I cNodes = atoi(rgszArg[1]);
        I cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT;
        PNODE pnodeCur;
        InitNodes(cNodes);
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
        {
            pnodeT = PnodeNew(iNode);
            InsertNext(pnodeCur, pnodeT);
            pnodeCur = pnodeT;
        }
        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur);
            }
            FreeNode(PnodeDeleteNext(pnodeCur));
        }
        printf("%d\n", Item(nodeCur));
    }

    I chose one of the variants that I personally find most attractive, any one of them could work. The thing about this example is that there are no comments.  That's reasonable given that it's an example from a textbook, and as such the words in the textbook that accompany the example suffice as the documentation.  But all code begs to be documented, so lets see what happens.

    When you look at documenting a function, there are a couple of things to keep in mind when considering style (there are other important things, like the relevance of the comments, etc, but this post's about style).

    The first question that comes to mind, almost immediately is "What commenting form should you use?"  For C/C++, there are a number of options.

    First off, C and C++ support two different styles of comment.  The first is the traditional  /* */ style of comments.  And there's the C++ style // comment.  Each of the two comments have different flavors.

    When I was in college, I loved using some of the options that /* */ comments enabled.  My code was just full of things like:

             :
             :
        }

        /*************************************************************/
        /*                                                           */
        /*                                                           */
        /*   Loop through the nodes looking for the current node.    */
        /*                                                           */
        /*                                                           */
        /*************************************************************/
        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
             :
             :
     

    I also had this thing about putting a comment on every line, especially for assignments.  And every comment lined up on column 40 if possible.

             :
             :
        }

        /*************************************************************/
        /*                                                           */
        /*                                                           */
        /*   Loop through the nodes looking for the current node.    */
        /*                                                           */
        /*                                                           */
        /*************************************************************/
        while (pnodeCur != PnodeNext(x))    // Keep looping until PnodeNext == pnodeCur
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++) // Skip through cNodesToSkip nodes
             :                             // A comment for this line.
             :                             // A comment for that line.
     

    When I look back at my old code, the code screams "1980's", just like the hairdos of "A Flock of Seagulls" does.  But there are times when big honking block comments like that are appropriate, like when you're pointing out something that's REALLY important - like when you're about to do something that would cause Raymond Chen to swear like a sailor when he runs into your application.

    Also, while I'm on the subject of comments on every line.  There ARE situations where you want a comment on every line.  For example, if you're ever forced to write assembly language (for any assembly language), you really MUST comment every line.  It's far too easy to forget which registers are supposed to contain what contents, so adding comments on every line is essential to understanding the code.

    /* */ comments have the disadvantage that they take up a fair amount of space.  This means that they increase the effective line length.  // comments don't have this problem.  On the other hand, /* */ comments stand out more.

    Personally I feel that both comment styles should be used.  One fairly effective form that I've seen is:

             :
             :
        }
        /*
         * Loop through the nodes until you hit the current node.
         */
        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur);  // Skip to the next node.
            }
             :
             :
     

    Of course that's not very different from:

             :
             :
        }

        // Loop through the nodes until you hit the current node.
        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur);  // Skip to the next node.
            }
             :
             :
     

    On the other hand, the first form (/**/) stands out more, simply because it has more white space.  You can achieve the same whitespace effect with // comments:

             :
             :
        }
        //
        // Loop through the nodes until you hit the current node.
        //
        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur);  // Skip to the next node.
            }
             :
             :
    Another aspect of commenting style is the effective line length.  When I learned to program, I learned on 80 character wide terminals, which imposed a fairly strict limit on the number of columns in the code - anything longer than 80 characters was not OK.  Nowadays, that isn't as important, but it's still important to limit source code lines to a reasonable number, somewhere around 100 works for me, but of course your experience might be different.

    The other major thing about comments is the content of the comments.  Comments can be the bane of a maintainers existence or their savior, depending on the comment.

    I don't know how many times I've come across the following and cringed:

        //
        // Increment i.
        //
        i = i + 1;
     

    Yes, the line's commented, but how useful is the comment?  Especially since it's a block comment (block comments have more importance than in-line comments by virtue of the amount of real estate they occupy - the very size of the comment gives it weight.  In general, you want to use in-line comments for little things and block comments for the big stuff.  I almost never use in-line comments, I prefer to use block comments at the appropriate locations and let the code speak for itself.

    I've mentioned white space as an aspect of style before, but this is the time to bring it up.  Consider the example routine with some white space added to stretch the code out a bit:

    #include "list.h"
    main(C cArg, SZ rgszArg[])
    {
        I iNode;
        I cNodes = atoi(rgszArg[1]);
        I cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT;
        PNODE pnodeCur;

        InitNodes(cNodes);

        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
        {
            pnodeT = PnodeNew(iNode);
            InsertNext(pnodeCur, pnodeT);
            pnodeCur = pnodeT;
        }

        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur);
            }
            FreeNode(PnodeDeleteNext(pnodeCur));
        }

        printf("%d\n", Item(nodeCur));
    }

    It's not a major change, but to me, by just inserting 4 empty lines, the code's been made clearer. 

    The other aspect of white space is intra-expression white space.  This is one of the aspects of style that tends to get religious.  I've seen people who do:

        for ( iNode = 2 , pnodeCur = PnodeNew( 1 ) ; iNode <= cNodes ; iNode ++ )
    And others who prefer:

        for (iNode=2,pnodeCur=PnodeNew(1);iNode<=cNodes;iNode++)
    Or:

        for (iNode=2, pnodeCur=PnodeNew(1); iNode<=cNodes; iNode++)
    Or:

        for ( iNode=2, pnodeCur=PnodeNew(1) ; iNode<=cNodes ; iNode++ )
    The reality is that it doesn't really matter which form you use, as long as you're consistent.

    Lets see what happens to the sample routine if you insert some block comments...

    #include "list.h"
    main(C cArg, SZ rgszArg[])
    {
        I iNode;
        I cNodes = atoi(rgszArg[1]);
        I cNodesToSkip = atoi(rgszArg[2]);
        PNODE pnodeT;
        PNODE pnodeCur;

        InitNodes(cNodes);

        //
        // Create a list of cNodes nodes. 
        // 
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
        {
            pnodeT = PnodeNew(iNode);
            InsertNext(pnodeCur, pnodeT);
            pnodeCur = pnodeT;
        }

        //
        // Walk the list of nodes, freeing the node that occurs at every cNodesToSkip nodes in the list.
        //
        while (pnodeCur != PnodeNext(x))
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur);
            }
            FreeNode(PnodeDeleteNext(pnodeCur));
        }

        //
        // Print out the value of the current node.
        //
        printf("%d\n", Item(nodeCur));
    }

    To me, that's a huge improvement.  By stretching out the code and adding some comments, it's already starting to look better.

    Again, just for grins, here's how it would look in my "1980's style":

    #include "list.h"
    main(C cArg, SZ rgszArg[])
    {
        I iNode;                              // Node index.
        I cNodes = atoi(rgszArg[1]);          // Set the number of nodes
        I cNodesToSkip = atoi(rgszArg[2]);    // and the nodes to skip.
        PNODE pnodeT;                         // Declare a temporary node.
        PNODE pnodeCur;                       // And the current node.

        InitNodes(cNodes);                    // Initialize the nodes database
                                              // with cNodes elements

        /*************************************************************/
        /*                                                           */
        /*                                                           */
        /*               Create a list of cNodes nodes.              */
        /*                                                           */
        /*                                                           */
        /*************************************************************/
        for (iNode = 2, pnodeCur = PnodeNew(1); iNode <= cNodes ; iNode++)
        {
            pnodeT = PnodeNew(iNode);        // Allocate a new node.
            InsertNext(pnodeCur, pnodeT);    // Insert it after the current node
            pnodeCur = pnodeT;               // Current = New.
        }

        /*************************************************************/
        /*                                                           */
        /*                                                           */
        /*   Walk the list of nodes, freeing the node that occurs    */
        /*   at every cNodesToSkip nodes in the list.                */
        /*                                                           */
        /*                                                           */
        /*************************************************************/
        while (pnodeCur != PnodeNext(x))    // While not at the current node
        {
            for (iNode = 1; iNode < cNodesToSkip ; iNode++)
            {
                pnodeCur = PnodeNext(pnodeCur); // Current = current->Next
            }
            FreeNode(PnodeDeleteNext(pnodeCur)); // Free current->Next
        }

        /*************************************************************/
        /*                                                           */
        /*                                                           */
        /*          Print out the value of the current node.         */
        /*                                                           */
        /*                                                           */
        /*************************************************************/
        printf("%d\n", Item(nodeCur));
    }

    Yech.

    Tomorrow: function and file headers.

  • Larry Osterman's WebLog

    Another pet peeve. Nounifying the word "ask"

    • 63 Comments

    Sorry about not blogging, my days are filled with meetings trying to finish up our LH beta2 features - I can't wait until people see this stuff, it's that cool.

    But because I'm in meetings back-to-back (my calender looks like a PM's these days), I get subjected to a bunch of stuff that I just hate.

    In particular, one "meme" that seems to have taken off here at Microsoft is nounifying the word "ask".

    I can't tell how many times I've been in a meeting and had someone say: "So what are your teams asks for this feature?" or "Our only ask is that we have the source process ID added to this message".

    For the life of me, I can't see where this came from, but it seems like everyone's using it.

    What's wrong with the word "request"?  It's a perfectly good noun and it means the exact same thing that a nounified "ask" means.

     

  • Larry Osterman's WebLog

    Threat Modeling, part 1

    • 16 Comments
    One of the requirements for designing software at Microsoft these days is that every (and I do mean EVERY) feature in every product needs to have a threat model.

    Threat modeling isn't new, it's been a part of good design for years.  Heck, the IETF has a threat model for the entire internet :) 

    Note that I didn't say good security design, IMHO, threat modeling isn't necessarily about security design.  Threat modeling's really all about understanding your design, at a level that design documents don't necessarily cover.  

    Threat modeling is almost more of a discipline than an analysis tool- if it's done right, it enables you to gain a much deeper understanding of your components and how they interact.  And by applying that methodology, you learn a great deal about your component.

    We're currently running through the threat modeling process in my group.  Since I'm the security nut on my team, I'm the one who volunteered to run the process.  And it's really been an interesting exercise..

    The big thing that writing the threat model for your feature (or component, or protocol, or whatever) is that it really forces you to understand the interactions between the various pieces of your feature.

    When threat modeling, you need to concentrate on a bunch of things.  First, there's your assets (also known as protected resources).  All threats relate to assets that need to be protected.  If you don't have assets, you don't have threats.  But sometimes the things that are your assets can be quite subtle.  For example, the audio samples being sent to a sound card might be an asset (especially if the audio samples came from DRM'ed files).  So might the privileges of the LocalSystem (or the current user) account.  The contents of a file on the disk might be an asset for a filesystem, similarly, the contents of an email message is an asset for an email system (like Exchange).  Understanding your assets is key - assets tend are attacked in different ways, the attack strategies mounted against an email message probably won't work for transient data like an audio sample.

    The next thing you need to determine is the entry points to your components.  The entry points to your components are what the attacker is going to use to gain access to your component.

    Related to entry points, one other thing that you need to determine are the trust boundaries in your component.  A trust boundary is a boundary (physical or virtual) across which there is a varied level of trust.  For example, when you RPC from one machine to another (or one process to another), that forms a trust boundary.  Another example of a trust boundary is the network - almost by definition, "the wire" is a trust boundary.

    For some systems, there are no real trust boundaries - an interactive application running as the user that only outputs data may have no trust boundaries.  Similarly, an API that processes data and returns it to the caller may have no trust boundaries.

    Related to trust boundaries are trust levels - trust levels indicates how much you "trust" a portion of the system.  For instance, if a user is an administrator, they are trusted to do more than normal users.  When data flows between entities whose trust level is different, by definition, there is a trust boundary between those two entities.

    Once you've identified your entry points, assets, trust boundaries, and trust levels, the next major part of a threat model is the data flow diagrams.  A data flow diagram indicates the flow of data into and out of the various parts of your components.

    All of this is the up-front work.  It lays the foundation for the "meat" of the threat model, which, of course are the threats.  I'll write more about threats tomorrow..

    One of the things that I realized while writing down all the stuff above is that getting all this stuff written down provides a really cool backup for your design documents.  Much of the time, design documents don't necessarily include all the interactions of the various pieces of the system (often they do, but...).  Threat modeling forces you to write those aspects of the design down.  It also forces you to think about (and write down on paper) the design of your component in terms of the DATA manipulated by your component, instead of the CODE that makes up your component.

     

    Btw, Frank Swiderski and Window Snyder have an excellent book on threat modeling, it's a bit dry reading, but it's really invaluable for learning about the process.  Microsoft has also provided a threat modeling tool (written by Frank) here that can be used to help guide you through the process of developing the threat model.  Microsoft's been working hard internally at making the threat modeling process require less effort - the ultimate goal of the team is "Do the DFD, turn the crank!".

    There are also lots of fascinating resources on the web available, including Dan Epp's article here, and Krishnan Ranganathan's threat model checklist here.  In addition, Michael Howard's written a bunch about TM, here and here.

    Edit: Included some additional information and links for Michael Howard's TM articles.

    Edit2: Corrected possesive on Dana Epp's name :)

  • Larry Osterman's WebLog

    A Tree Grows... How?

    • 100 Comments
    Valorie's currently attending a seminar about teaching science at the middle-school level.

    Yesterday, her instructor asked the following question:

    "I have in my hand a Douglass Fir tree seed that masses 1 gram [I'm making this number up, it's not important].  I plant it in my yard, water it regularly, and wait for 20 years.

    At the end of that time, I have a 50 foot tree that masses 1,000 kilograms [again, I'm making this exact number up, it's not important].

     

    My question is: Where did the 999,999 grams of mass come from?"

     

    I'm going to put the answer out to the group.  Where DID the 999,999 grams of mass come from in the tree.

    The answer surprises a lot of people.  And it brings to question how much we actually know about science.

     

     

    I'm going to change my comment moderation policy for this one.  I'm NOT going to approve people whose comments have the right answer, until I post my follow-up post tomorrow, because once the right answer's been given, it's pretty clear.  But I'll be interested in knowing the percentage of comments that have the right answer vs. the wrong answer.

     

  • Larry Osterman's WebLog

    Threat Modeling, once again

    • 23 Comments

    About 2.5 years ago, I wrote a series of articles about how we threat model at Microsoft, about 18 months ago, I made a couple of updates to it, including a post about why we threat model at Micrososoft, and a review of how the process has changed over the years.

    It's threat modeling time again in my group (it seems to happen about once every 18 months or so, as you can see from my post history :)), and as the designated security nutcase in my group, I've been spending a LOT of time thinking about the threat modeling process as we're practicing it nowadays.  It's been interesting looking at my old posts to see how my own opinions on threat modeling have changed, and how Microsoft's processes have changed (we've gotten a LOT better at the process).

    One thing that was realized very early on is that our early efforts at threat modeling were quite ad-hoc.  We sat in a room and said "Hmm, what might the bad guys do to attack our product?" It turns out that this isn't actually a BAD way of going about threat modeling, and if that's all you do, you're way better off than you were if you'd done nothing.

    Why doesn't it work?  There are a couple of reasons:

    1. It takes a special mindset to think like a bad guy.  Not everyone can switch into that mindset.  For instance, I can't think of the number of times I had to tell developers on my team "It doesn't matter that you've checked the value on the client, you still need to check it on the server because the client that's talking to your server might not be your code.".
    2. Developers tend to think in terms of what a customer needs.  But many times, the things that make things really cool for a customer provide a superhighway for the bad guy to attack your code. 
    3. It's ad-hoc.  Microsoft asks every single developer and program manager to threat model (because they're the ones who know what the code is doing).  Unfortunately that means that they're not experts on threat modeling. Providing structure helps avoid mistakes.

    So how do we go about threat modeling?

    Well, as the fictional Maria Von Trapp said in her famous introductory lesson to solfege, "Let's start at the very beginning, A very good place to start"...

     

    One of the key things we've learned during the process is that having a good diagram is key to a good threat model.  If you don't have a good diagram, you probably don't have a good threat model.

    So how do you go about writing a good diagram?

    The first step is to draw a whiteboard diagram of the flow of data in your component.  Please note: it's the DATA flow you care about, NOT the code flow.  Your threats come via data, NOT code.  This is the single most common mistake that people make when they start threat modeling (it's not surprising, because as developers, we tend to think about code flow).

    When you're drawing the whiteboard diagram, I use the following elements (you can choose different elements, the actual image doesn't matter, what matters is that you define a common set of elements for each type):

    Element Image Element Type What the heck is this thing?
    image External Interactor An external interactor is an element that is outside your area of control.  It could be a user calling into an API, it could be another component but not one that's being threat modeled.  For example, if you're threat modeling an API, than the application which invoked the API is an external entity.  On the other hand, if you're threat modeling an application that calls into an API, the API is an external entity
    image Process A "process" is simply some code.  It does NOT mean that it's a "process" as OS's call processes, instead it's just a collection of code.
    image Multiple Process A "multiple process" is used when your threat model is complex enough to require multiple DFDs (this is rarely the case, but does happen).  In that case, the "multiple process" is expanded in the other DFD.  I'm not sure I've ever seen a threat model that used a "multiple process" element - you can usually break out everything that you want to break down, so they're very rarely seen.
    image Data Store A datastore is something that holds data.  It could be a file, a registry key, or even a shared memory region.
    image Data Flow A dataflow represents the flow of data through the system.  Please note that it does NOT represent the flow of code, but that of data.
    image Trust Boundary A trust boundary occurs when one component doesn't trust the component on the other side of the boundary.  There is always a trust boundary between elements running at different privilege levels, but there sometimes are trust boundaries between different components running at the same privilege level.

    image

    Machine Boundary A machine boundary occurs when data moves from one machine to another.
    image Process Boundary A process boundary occurs when data moves from one OS process to another. 

     

    You build a data flow diagram by connecting the various elements by data flows, inserting boundaries where it makes sense between the elements.

     

     Now that we have a common language, we can using it to build up a threat model.

    Tomorrow: Drawing the DFD.

  • Larry Osterman's WebLog

    Running Non Admin

    • 38 Comments

    There’s been a fascinating process going on over here behind the curtains.  With the advent of XP SP2, more and more people are running as non administrative users.  Well, it’s my turn to practice what I preach, I’ve taken the plunge on my laptop and my home machine, I’m now running as a non admin user (I can’t do it on my development machine at work for the next few weeks for a variety of reasons).

    The process so far has been remarkably pain free, but there have been some “interesting” idiosyncrasies.  First off, I’ve been quite surprised at the number of games that have worked flawlessly.  I was expecting to have major issues, but none so far, with the exception of Asheron’s Call.  Annoyingly, the problem with AC isn’t the game itself, it’s with Microsoft’s Gaming Zone software, which insists on modifying files in the C:\Program Files directory. 

    Aaron Margosis’ blog posts about running as a limited user have been extremely helpful as well.

    Having said that, there are some oddities I’ve noticed.  First off: There seem to be a lot of applications that “assume” that they know what the user’s going to do.  For instance, if you double click on the time in the system tray, it pops up with “You don’t have the proper privilege level to change the System Time”.  This is a completely accurate statement, since modifying the time requires the SeSystemTime privilege, which isn’t granted to limited users.  But it assumes that the reason that I was clicking on the time was to change the time.  But maybe I wanted to use the date&time control panel as a shortcut to the calendar?  I know of a bunch of users that call action of double clicking on the time in the taskbar as invoking the “cool windows calendar”, they don’t realize that they’re just bringing up the standard date&time applet.  If I don’t have the SeSystemTime privilege, then why not just grey out the “OK” button?  Let me navigate the control but just prevent me from changing things.

    Similarly, the users control panel applet prompts you with a request to enter your credentials.  Why?  There are lots of things a limited user can do with the users control panel applet (enumerating groups, enumerating users, enumerating group membership, setting user information).  But the control panel applet ASSUMES that the user wants to manipulate the state of the other users.  It’s certainly true that most of the useful functionality of that control panel applet requires admin access.  But it should have waited until the user attempted to perform an action that was denied before it prompted the user for admin credentials.

    From my standpoint, these examples violate two of the principals of designing interfaces that involve security: 

    1)      Don’t tell the user they can’t do something until you’ve proven that they can’t do it.

    2)      Don’t assume what access rights the user needs to perform an operation. 

    The date&time control panel violates the first principal.  The user might be interacting with the control panel for reasons other than changing the time.  It turns out that the reason for this is that the date&time applet violates the principle of least privilege by enabling the SeDateTime privilege, running the control panel applet, and then disabling the privilege.  If the control panel applet had waited until the user clicked on the “Apply” button before it enabled the privilege (and then failed when the enable privilege failed), it would have better served the user IMHO.

    The users control panel applet violates the second principal.  In the case of the users control panel, it assumed that I was going to do something that required admin access.   This may in fact be a reasonable assumption given the work that the users control panel applet does (its primary task is to manage local group membership).  But the applet assumes up front that the user has to be an administrator to perform the action.  There may in fact be other classes of users that can access the information in the users control panel – as an example, members of the domains’ “account operators” group may very well be able to perform some or all the actions that the users control panel applet performs.  But the control panel applet doesn’t check for that – it assumes that the user has to be a member of the local administrators group to use the control panel applet.  Interestingly enough, this behavior only happens on XP PRO when joined to a domain.  If you’re not joined to a domain, the users control panel applet allows you to change your user information without prompting you – even as a limited user.   Peter Torr also pointed out that the computer management MCC snapin (compmgmt.msc) does the “right” thing – you can interact with the UI, perform actions (adding users to groups, etc), and it’s only when you click the “Apply” button that it fails.  The snap-in doesn’t know what’s allowed or not, it just tries the operation, and reports the failure to the user.

    This is a really tough problem to solve from a UI perspective – you want to allow the user to do their work, but it’s also highly desirable that you not elevate the privilege of the user beyond the minimum that’s required for them to do their job.  The good news is that with more and more developers (both at Microsoft and outside Microsoft) running as non administrative users, more and more of these quirks in the system will be fixed.

     

    Edit: Thanks Mike :)
  • Larry Osterman's WebLog

    What's this untitled slider doing on the Vista volume mixer?

    • 80 Comments

    Someone sent the following screen shot to one of our internal troubleshooting aliases.  They wanted to know what the "Name Not Available" slider meant.

    clip_image002[7]

     

    The audio system on Vista keeps track of the apps that are playing sounds (it has to, to be able to display the information on what apps are playing sounds :)).  It keeps this information around for a period of time after the application has made the sound to enable the scenario where your computer makes a weird sound and you want to find out which application made the noise.

    The system only keeps track of the PID for each application, it's the responsibility of the volume mixer to convert the PID to a reasonable name (the audio service can't track this information because of session 0 isolation).

    This works great, but there's one possible problem: If an application exits between the time when the application made a noise and the system times out the fact that it played the noise, then the volume mixer has no way of knowing what the name of the application that made the noise was. In that case, it uses the "Name Not Available" text to give the user some information.

  • Larry Osterman's WebLog

    Larry goes to Layer Court

    • 29 Comments
    Two weeks ago, my boss, another developer in my group, and I had the opportunity to attend "Layer Court".

    Layer Court is the end product of a really cool part of the quality gate process we've introduced for Windows Vista.  This is a purely internal process, but the potential end-user benefits are quite cool.

    As systems get older, and as features get added, systems grow more complex.  The operating system (or database, or whatever) that started out as a 100,000 line of code paragon of elegant design slowly turns into fifty million lines of code that have a distinct resemblance to a really big plate of spaghetti.

    This isn't something specific to Windows, or Microsoft, it's a fundamental principal of software engineering.  The only way to avoid it is extreme diligence - you have to be 100% committed to ensuring that your architecture remains pure forever.

    It's no secret that regardless of how architecturally pure the Windows codebase was originally, over time, lots of spaghetti-like issues have crept into the  product over time.

    One of the major initiatives that was ramped up with the Longhorn Windows Vista reset was the architectural layering initiative.  The project had existed for quite some time, but with the reset, the layering team got serious.

    What they've done is really quite remarkable.  They wrote tools that perform static analysis of the windows binaries and they work out the architectural and engineering dependencies between various system components.

    These can be as simple as DLL dependencies (program A references DLLs B and C, DLL B references DLL D, DLL D in turn references DLL C), they can be as complicated as RPC dependencies (DLL A has a dependency on process B because DLL A contacts an RPC server that is hosted in process B).

    The architectural layering team then went out and assigned a number to every single part of the system starting at ntoskrnl.exe (which is the bottom, at layer 0).

    Everything that depended only on ntoskrnl.exe (things like win32k.sys or kernel32.dll) was assigned layer 1 , the pieces that depend on those (for example, user32.dll) got layer 2, and so forth (btw, I'm making these numbers up - the actual layering is somewhat more complicated, but this is enough to show what's going on).

    As long as the layering is simple, this is pretty straightforward.  But then the spaghetti problem starts to show up.  Raymond may get mad, but I'm going to pick on the shell team as an example of how a layering violation can appear.  Consider a DLL like SHELL32.DLL.  SHELL32 contains a host of really useful low level functions that are used by lots of applications (like PathIsExe, for example).  These functions do nothing but string manipulation of their input functions, so they have virtually no lower level dependencies.   But other functions in SHELL32 (like DefScreenSaverProc or DragAcceptFiles) manipulate windows and interact with large number of lower components.  As a result of these high level functions, SHELL32 sits relatively high in the architectural layering map (since some of its functions require high level functionality).

    So if relatively low level component (say the Windows Audio service) calls into SHELL32, that's what is called a layering violation - the low level component has taken an architectural dependency on a high level component, even if it's only using the low level functions (like PathIsExe). 

    They also looked for engineering dependencies - when low level component A gets code that's delivered from high level component B - the DLLs and other interfaces might be just fine, but if a low level component A gets code from a higher level component, it still has a dependency on that higher level component - it's a build-time dependency instead of a runtime dependency, but it's STILL a dependency.

    Now there are times when low level components have to call into higher level components - it happens all the time (windows media player calls into skins which in turn depend on functionality hosted within windows media player).  Part of the layering work was to ensure that when this type of violation occurred that it fit into one of a series of recognized "plug-in" patterns - the layering team defined what were "recognized" plug-in design patterns and factored this into their analysis.

    The architectural layering team went through the entire Windows product and identified every single instance of a layering violation.  They then went to each of the teams, in turn and asked them to resolve their dependencies (either by changing their code (good) or by explaining why their code matches the plugin pattern (also good), or by explaining the process by which their component will change to remove the dependency (not good, because it means that the dependency is still present)).   For this release, they weren't able to deal with all the existing problems, but at least they are preventing new ones from being introduced.  And, since there's a roadmap for the future, we can rely on the fact that things will get better in the future.

    This was an extraordinarily painful process for most of the teams involved, but it was totally worth the effort.  We now have a clear map of which Windows components call into which other Windows components.  So if a low level component changes, we can now clearly identify which higher level components might be effected by that change.  We finally have the ability to understand how changes ripple throughout the system, and more importantly, we now have mechanisms in place to ensure that no lower level components ever take new dependencies on higher level components (which is how spaghetti software gets introduced).

    In order to ensure that we never introduce a layering violation that isn't understood, the architectural layering team has defined a "quality gate" that ensures that no new layering violations are introduced into the system (there are a finite set of known layering violations that are allowed for a number of reasons).  Chris Jones mentioned "quality gates" in his Channel9 video, essentially they are a series of hurdles that are placed in front of a development team - the team is not allowed to check code into the main Windows branches unless they have met all the quality gates.  So by adding the architectural layering quality gate, the architectural layering team is drawing a line in the sand to ensure that no new layering violations ever get added to the system.

    So what's this "layer court" thingy I talked about in the title?  Well, most of the layering issues can be resolved via email, but for some set of issues, email just doesn't work - you need to get in front of people with a whiteboard so you can draw pretty pictures and explain what's going on.  And that's where we were two weeks ago - one of the features I added for Beta2 restored some functionality that was removed in Beta1, but restoring the functionality was flagged as a layering violation.  We tried, but were unable to resolve it via email, so we had to go to explain what we were doing and to discuss how we were going to resolve the dependency.

    The "good" news (from our point of view) is that we were able to successfully resolve the issue - while we are still in violation, we have a roadmap to ensure that our layering violation will be fixed in the next release of Windows.  And we will be fixing it :)

     

  • Larry Osterman's WebLog

    Where do "checked" and "free" come from?

    • 26 Comments

    People who have MSDN or the DDK know that Windows is typically built in two different flavors, "Checked" and "Free".  The primary difference between the two is that the "checked" build has traces and asserts, but the free build doesn't.

    Where did those names "checked" and "free" come from?  It's certainly not traditional, the traditional words are "Debug" and "Retail" (or "Release").

    When we were doing the initial development of Windows NT, we started by using the same "Debug" and "Retail" names that most people use.

    The thing is, it turns out that there are actually four different sets of options that make up the "Debug" and "Retail" split.

    You have:

    1. Compiler Optimization: On/Off
    2. Debug Traces: On/Off
    3. Assertions: Enabled/Disabled
    4. Sanity checks: Enabled/Disabled

    Traditionally, "Debug" is "Optimization:off, Traces:on, Assertions: on" and "Retail" is "Optimization:on, Traces:off, Assertions: off".  Sanity checks was something the NT team added.  The idea was that there would be additional sanity checks built in for our internal development that would be removed before we shipped.

    So the NT build team wanted to build "Optimization:on, Traces:on, Assertions: on, sanity checks:on" and "Optimizations:on, traces:off, assertions: off, sanity checks: on" and "optimizations:on, traces:off, assertions:off, sanity checks: off".

    The last was what was traditionally called "Retail" - no debugging whatsoever.  However, the question still remained - what to call the "O:on, T:on, A:on, S:on" and "O:on, T:off, A:off, S:on" build - the first wasn't "Debug" because the optimizer was enabled, the latter wasn't "Retail", since the sanity checks were enabled.

    So clearly there needed to be some other name to differentiate these cases.  After some internal debate, we settled on "Checked" for the "O:on, T:on, A:on, S:on" and "Free" for the "O:on, T:off, A:off, S:on" build.  Checked because it had all the checks enabled, and free because it was "check free".

    And as the NT 3.1 project progressed, the team eventually realized that (a) since they'd never actually tested the "retail" build, they had no idea what might break when they started making builds, and (b) since they had perf tested the free build and it met the perf criteria, the team eventually decided to ship the free build as the final version.

     

     

  • Larry Osterman's WebLog

    Moore's Law Is Dead, Long Live Moore's law

    • 44 Comments
    Herb Sutter has an insightful article that will be published in Dr. Dobb's in March, but he's been given permission to post it to the web ahead of time.  IMHO, it's an absolute must-read.

    In it, he points out that developers will no longer be able to count on the fact that CPUs are getting faster to cover their performance issues.  In the past, it was ok to have slow algorithms or bloated code in your application because CPUs got exponentially faster - if you app was sluggish on a 2GHz PIII, you didn't have to worry, the 3GHz machines would be out soon, and they'd be able to run your code just fine.

    Unfortunately, this is no longer the case - the CPU manufacturers have hit a wall, and are (for the foreseeable future) unable to make faster processors.

    What does this mean?  It means that (as Herb says) the free lunch is over. Intel (and AMD) isn't going to be able to fix your app's performance problems, you've got to fall back on solid engineering - smart and efficient design, extensive performance analysis and tuning.

    It means that using STL or other large template libraries in your code may no longer be acceptable, because they hide complexity.

    It means that you've got to understand what every line of code is doing in your application, at the assembly language level.

    It means that you need to investigate to discover if there is inherent parallelism in your application that you can exploit.  As Herb points out, CPU manufacturers are responding to the CPU performance wall by adding more CPU cores - this increases overall processor power, but if your application isn't designed to take advantage of it, it won't get any faster.

    Much as the financial world enjoyed a 20 year bull market that recently ended (ok, it ended in 1999), the software engineering world enjoyed a 20 year long holiday that is about to end. 

    The good news is that some things are still improving - memory bandwidth continues to improve, hard disks are continuing to get larger (but not faster).  CPU manufacturers are going to continue to add more L1 cache to their CPUs, and they're likely to continue to improve.

    Compiler writers are also getting smarter - they're building better and better optimizers, which can do some really quite clever analysis of your code to detect parallelisms that you didn't realize were there.  Extensions like OpenMP (in VS 2005) also help to improve this.

    But the bottom line is that the bubble has popped and now it's time to pay the piper (I'm REALLY mixing metaphors today).  CPU's aren't going to be getting any faster anytime soon, and we're all going to have to deal with it.

    This posting is provided "AS IS" with no warranties, and confers no rights.

  • Larry Osterman's WebLog

    Windows Vista Sound causes Network Throughput slowdowns.

    • 62 Comments

    AKA: How I spent last week :).

    On Tuesday Morning last week, I got an email from "reader@slashdot.org":

    You've probably already seen this article, but just in case I'd love to hear your response.

    http://it.slashdot.org/article.pl?sid=07/08/21/1441240

    Playing Music Slows Vista Network Performance?

    In fact, I'd not seen this until it was pointed out to me.  It seemed surprising, so I went to talk to our perf people, and I ran some experiments on my own.

    They didn't know what was up, and I was unable to reproduce the failure on any of my systems, so I figured it was a false alarm (we get them regularly).  It turns out that at the same time, the networking team had heard about the same problem and they WERE able to reproduce the problem.  I also kept on digging and by lunchtime, I'd also generated a clean reproduction of the problem in my office.

    At the same time, Adrian Kingsley-Hughes over at ZDNet Blogs picked up the issue and started writing about the issue.

    By Friday, we'd pretty much figured out what was going on and why different groups were seeing different results - it turns out that the issue was highly dependent on your network topology and the amount of data you were pumping through your network adapter - the reason I hadn't been able to reproduce it is that I only have a 100mbit Ethernet adapter in my office - you can get the problem to reproduce on 100mbit networks, but you've really got to work at it to make it visible.  Some of the people working on the problem sent a private email to Adrian Kingsley-Hughes on Friday evening reporting the results of our investigation, and Mark Russinovich (a Technical Fellow, and all around insanely smart guy) wrote up a detailed post explaining what's going on in insane detail which he posted this morning.

    Essentially, the root of the problem is that for Vista, when you're playing multimedia content, the system throttles incoming network packets to prevent them from overwhelming the multimedia rendering path - the system will only process 10,000 network frames per second (this is a hideously simplistic explanation, see Mark's post for the details)

    For 100mbit networks, this isn't a problem - it's pretty hard to get a 100mbit network to generate 10,000 frames in a second (you need to have a hefty CPU and send LOTS of tiny packets), but on a gigabit network, it's really easy to hit the limit.

     

    One of the comments that came up on Adrian's blog was a comment from George Ou (another zdnet blogger):

    ""The connection between media playback and networking is not immediately obvious. But as you know, the drivers involved in both activities run at extremely high priority. As a result, the network driver can cause media playback to degrade."


    I can't believe we have to put up with this in the era of dual core and quad core computers. Slap the network driver on one CPU core and put the audio playback on another core and problem solved. But even single core CPUs are so fast that this shouldn't ever be a problem even if audio playback gets priority over network-related CPU usage. It's not like network-related CPU consumption uses more than 50% CPU on a modern dual-core processor even when throughput hits 500 mbps. There’s just no excuse for this."

    At some level, George is right - machines these days are really fast and they can do a lot.  But George is missing one of the critical differences between multimedia processing and other processing.

    Multimedia playback is fundamentally different from most of the day-to-day operations that occur on your computer. The core of the problem is that multimedia playback is inherently isochronous. For instance, in Vista, the audio engine runs with a periodicity of 10 milliseconds. That means that every 10 milliseconds, it MUST wake up and process the next set of audio samples, or the user will hear a "pop" or “stutter” in their audio playback. It doesn’t matter how fast your processor is, or how many CPU cores it has, the engine MUST wake up every 10 milliseconds, or you get a “glitch”.

    For almost everything else in the system, if the system locked up for even as long as 50 milliseconds, you’d never notice it. But for multimedia content (especially for audio content), you absolutely will notice the problem. The core reason behind it has to do with the physics of sound, but whenever there’s a discontinuity in the audio stream, a high frequency transient is generated. The human ear is quite sensitive to these high frequency transients (they sound like "clicks" or "pops"). 

    Anything that stops the audio engine from getting to run every 10 milliseconds (like a flurry of high priority network interrupts) will be clearly perceptible. So it doesn’t matter how much horsepower your machine has, it’s about how many interrupts have to be processed.

    We had a meeting the other day with the networking people where we demonstrated the magnitude of the problem - it was pretty dramatic, even on the top-of-the-line laptop.  On a lower-end machine it's even more dramatic.  On some machines, heavy networking can turn video rendering to a slideshow.

     

    Any car buffs will immediately want to shoot me for this analogy, because I’m sure it’s highly inaccurate (I am NOT a car person), but I think it works: You could almost think of this as an engine with a slip in the timing belt – you’re fine when you’re running the engine at low revs, because the slip doesn’t affect things enough to notice. But when you run the engine at high RPM, the slip becomes catastrophic – the engine requires that the timing be totally accurate, but because it isn’t, valves don’t open when they have to and the engine melts down.

     

    Anyway, that's a long winded discussion.  The good news is that the right people are actively engaged on working to ensure that a fix is made available for the problem.

  • Larry Osterman's WebLog

    Interacting with Services

    • 15 Comments
    In the comments for my first services post, someone asked about the SERVICE_INTERACTIVE_PROCESS flag that can be specified for the CreateService API.

    This flag allows the user to specify that the service should be allowed to interact with the logged on user.  The idea of an interactive service was added back in NT 3.1 for components like printer drivers that want to pop up UI.

    IMHO this was a spectacularly bad idea that should never have been added to the system.

    MSDN has an entire page on interactive services, unfortunately IMHO, it doesn't go into enough detail as to why it's a bad idea to ever specify the SERVICE_INTERACTIVE_PROCESS flag on a service.

    The primary reason for this being a bad idea is that interactive services enable a class of threats known as "Shatter" attacks (because they "shatter windows", I believe). 

    If you do a search for "shatter attack", you can see some details of how these security threats work.  Microsoft also published KB article 327618 which extends the documentation about interactive services, and Michael Howard wrote an article about interactive services for the MSDN Library.  Initially the shatter attacks went after windows components that had background window message pumps (which have long been fixed), but they've also been used to attack 3rd party services that pop up UI.

    The second reason it's a bad idea is that the SERVICE_INTERACTIVE_PROCESS flag simply doesn't work correctly.  The service UI pops up in the system session (normally session 0).  If, on the other hand, the user is running in another session, the user never sees the UI.  There are two main scenarios that have a user connecting in another session - Terminal Services, and Fast User Switching.  TS isn't that common, but in home scenarios where there are multiple people using a single computer, FUS is often enabled (we have 4 people logged in pretty much all the time on the computer in our kitchen, for example).

    The third reason that interactive services is a bad idea is that interactive services aren't guaranteed to work with Windows Vista :)  As a part of the security hardening process that went into Windows Vista, interactive users log onto sessions other than the system session - the first interactive user runs in session 1, not session 0.  This has the effect of totally cutting shatter attacks off at the knees - user apps can't interact with high privilege windows running in services. 

     

    On the other hand, sometimes it's important to interact with the logged on user.  How do you deal with this problem?  There are a couple of suggestions as to how to resolve the issue.  The first is to use the CreateProcessAsUser API to create a process on the users desktop.  Since the new process is running in the context of the user, privilege elevation attacks don't apply.  Another variant of this solution is to use an existing systray process to communicate with the service.

    In addition, if a COM object is marked as running in the security context of the interactive user, it will be activated in the interactive user's session.  You can use a session moniker to start a COM object in a particular session.  There's an example of how to do this here.

     

  • Larry Osterman's WebLog

    Compressible Encryption

    • 23 Comments

    Time to spread a smidge of dirt on Microsoft :).

    One of my favorite dialog boxes is found in Outlook.  If you dig deep enough into your email accounts, you'll find the following dialog box:

      Outlook Offline File Settings Dialog

    The reason I like this dialog box is the default setting "Compressible Encryption".  Why?  Because if you select it, you're not encrypting ANYTHING.  "Compressible Encryption" is really "compressed".  When this option is selected, the data in the OST in specified is compressed (I'm not sure of the algorithm).

    Calling a compressed OST file "encrypted" is sort of like saying that a ZIP file is an encrypted version of the file.  After all, if you look at the contents of the ZIP file, you'll not find any the information directly represents the original file (ok, the filenames might be in the archive uncompressed but that's about it).  But of course it's not encrypted.

    If you specify "High Encryption" then you get a truly encrypted OST file.  I'm not sure of the algorithms they use, but it IS really encrypted.

    So why on earth do they call it compressible encryption?  Well, I'm not 100% sure, but I suspect that the answer is that some executive decided to type their PST file (or OST file) and noticed that their email was found in clear text within the file.

    They also noticed that if they used compression on the PST file, then they weren't able to see the contents of the file.  So they equated compression with encryption (hey, they couldn't see the data, could they?).  And thus "compressible encryption" was born.

    It's really just a silly affectation - they should never have called it "encryption" because someone might think that the data's actually hidden, but...  If the dialog was being designed today (the actual dialogs over 10 years old), the term "encryption" would never be used but nowadays it's sort-of a historical oddity.

    If you do a search for "compressible encryption", the first Google and MSN search hit is Leo Notenboom's article on compressable encryption, here's the official KB article on compressible encryption.

    There are other examples where similar obfuscation has occurred, and I'm sure that other vendors have done similar things (or worse).  For example, the Exchange MAPI client-to-Exchange Server protocol is obfuscated because an executive noticed that if he took a network sniff of the traffic between his client and the Exchange server he could see his email messages going by.  So he asked the team to obfuscated the stream - we knew that it did nothing, and so did the executive, but as he pointed out, it's enough to protect from casual attackers.  If you really want encrypted communications, then if you specify the "Encrypt data between Microsoft Office Outlook and Microsoft Exchange Server" option in the Security tab of the Microsoft Exchange Server dialog, then that specifies RPC_C_AUTHN_LEVEL_PKT_PRIVACY, which uses a encryption mechanism to protect the data (I believe it's DES-56 but I'm not 100% sure).  I believe that this option is the default in all current versions of Outlook, but I'm not 100% sure.

  • Larry Osterman's WebLog

    The Endian of Windows

    • 17 Comments

    Rick's got a great post on what big and little endian are, and what the Apple switch has to do with Word for the Mac.

    In the comments, Alicia asked about Windows...

    I tried to make this a comment on his blog but the server wouldn't take it.

     

    The answer is pretty simple: For Windows, there are parts of Windows that are endian-neutral (for instance, the CIFS protocol handlers and all of the DCE RPC protocol, etc), but the vast majority of Windows is little-endian.

    A decision was made VERY long ago that Windows would not be ported to a big-endian processor.  And as far as I can see, that's going to continue.  Since almost all the new processors coming out are either little-endian, or swing both ways (this is true of all the RISC machines Windows has supported, for example), this isn't really a big deal.

  • Larry Osterman's WebLog

    So what's wrong with DRM in the platform anyway?

    • 53 Comments

    As I said yesterday, it's going to take a bit of time to get the next article in the "cdrom playback" series working, so I thought I'd turn the blog around and ask the people who read it a question.

    I was reading Channel9 the other day, and someone turned a discussion of longhorn into a rant against the fact that Longhorn's going to be all about DRM (it's not, there will be DRM support in Longhorn, just like there has been DRM support in just about every version of Windows that's distributed windows media format).

    But I was curious.  Why is it so evil that a platform contain DRM support?

    My personal opinion is that DRM is a tool for content producers.  Content Producers are customers, just like everyone else that uses our product is a customer.  They want a platform that provides content protection.  You can debate whether or not that is a reasonable decision, but it's moot - the content producers today want it.

    So Microsoft, as a platform vendor provides DRM for the content producers.  If we didn't, they wouldn't use our media formats, they'd find some other media format that DOES have DRM support for their content.

    The decision to use (or not use) DRM is up to the content producer.  It's their content, they can decide how to distribute it.  You can author and distribute WMA/WMV files without content protection - all my ripped CDs are ripped without content protection (because I don't share them).  I have a bunch of WMV files shot on the camcorder that aren't DRM'ed - they're family photos, there's no point in using rights management.

    There are professional content producers out there that aren't using DRM for their content (Thermal and a Quarter is a easy example I have on the tip of my tongue (as I write this, they've run out of bandwidth :( but...)).  And there are content producers that are using DRM.

    But why is it evil to put the ability to use DRM into the product?

  • Larry Osterman's WebLog

    Bobs Math Question: The Official Answers

    • 23 Comments

    EDIT: Please note: This is a single post explaining the answer to a question posted earlier on this blog. 

    This site is NOT intended as a general purpose site in which to get help with your math homework.

    If you're having problems with your math homework, then you should consider asking your parents for help, you're not likely to find it here, sorry about that.

     


    Ok, he's back :)  My last post was a math problem that the teacher in my wife's classroom gave to the students (mostly 11 and 12 year olds fwiw).

    Here's the official answer to the problem, the kids needed to show ALL the calculations (sorry for the word-junk):


    Pyramid L=W=2’ H2 = 22 – 12 so H = 1.73

    V        =1/3*l*w*h

    = 1/3*2*2*1.73 = 2.31 cubic feet

    SA     =b2 + 2bh

    = (2)2 + 2*(2)*1.73

    = 4 + 6.92 = 10.92 square feet.

     

    Triangles

    V=B*h   SA = front + back + 3 sides

    = 2*(1/2*l*h) + 3* L*W

    Triangle #1 : L=8’, W=2’ H2 = 82 – 42 H = 6.93

    V = 1/2*8*6.93*2 = 55.44 cubic feet

    SA = 2(1/2*8*6.93) + 3*8*2 = 103.44 square feet

     

    Triangle #2 : L=9’, W=2’ H2 = 92 – 4.52 H = 7.79

    V = 1/2*9*7.79*2 = 70.11 cubic feet

    SA = 2(1/2*9*7.79) + 3*9*2 = 124.11 square feet

     

    Triangle #3 : L=10’, W=2’ H2 = 102 – 52 H = 8.66

    V = 1/2*10*8.66*2 = 86.6 cubic feet

    SA = 2(1/2*10*8.66) + 3*10*2 = 146.6 square feet

     

    Base of Tree: L=W=2’  H= 3’

    V = L*W*H = 2*2*3 = 12 cubic feet

    SA     = 2(L*H) + 2(W*H) + 2(L*W)

              = 2(2*3 + 2*3 + 2*2)

              = 2(6 + 6 + 4)

              = 32 square feet

     

    6 cones with H=1’, R=.5’, S= 1.12’

    V = 1/3*π*r2h = 1/3 * 3.14*.52 * 1 = .26 cubic feet

    Total volume = 6*.26 = 1.56 cubic feet

    Volume before cutouts:

    Pyramid                    2.31

    Triangle #1           55.44

    Triangle #2           70.11

    Triangle #3           86.60

    Base                        12.00

    Cones                        1.56

    TOTAL                  228.02

                                 Cubic feet

     

     Surface Area before cutouts:

    Pyramid                   10.92

    Triangle #1           103.44

    Triangle #2           124.11

    Triangle #3           146.60

    Base                        32.00

    Cones                      15.30

    TOTAL                  432.37

    Square

     


    Cutout Calculations - Volume

    All of the volume of the cutouts are subtracted from the total volume of the Christmas tree.

     

    There are 6 cylinders total.

    1 has r=1, h=2

    4 have r=1.5, h=2

    1 has r=2, h=2

     

    V = πr2h       SA = 2πr2 + 2πrh

    V        = π*(12 + 4(1.52) + 22)*2

              = π*(1+9+4)*2

              = 3.14*14*2 = 87.92 cubic feet

     

    Small Triangular Prisms

    There are three triangular prisms.

    1 has L=B=1 and W = 2’

    H2 = 12 - .52 so H= .87’

    2 have L=B=1.5 and W = 2

              H2 = 1.52 - .752 so H = 1.69’

     

    V        = Bw where B=1/2*l*h

    V        = (1/2*1*.87*2) + 2*(1/2*1.5*1.69*2)

              = .87 + 5.07

              = 5.94 cubic feet

     

    Total volume to subtract:

    87.92

    +5.94

    93.86 cubic feet

     

    Christmas tree volume minus cutouts:

              228.02

              -93.86

    134.16 Cubic Feet total


    Cutout Calculations – SA

    The front and back SA’s are subtracted from the total SA of the Christmas Tree but the side SA’s are added to the total.

     

    Cylinders

    Front and back SA = 2πr2

    Side SA = 2πrh

    Front and Back SA

              = 2π(12 + 4*1.52 + 22)

              =6.28 * (1+9+4)

              = 87.92 Square feet

    Side SA

              = 2πrh

              =2*π*(1+4*1.5+2)*2

              = 12.56 * 9 = 113.04 Square feet

    Small Triangular Prisms

    Front and Back SA

    = 2*1/2*b*h

    = b*h

    = 1*.87 + 2(1.5*1.69)

    = .87 + 5.07

    = 5.91 Square Feet

     

    Side SA

              = 3*b*w

              = 3*(1+1.5+1.5)*2

              = 24 square feet

    Twice the SA of top of Base

              =2(2*2)=8 Square Feet

     

    SA to Add:            137.04

    SA to Subtract:      101.83

    Total SA to add:      35.21

     

    Christmas Tree SA plus cutouts:

              432.37

              +35.21

              467.58 Square Feet Total

    Edit: Reduced Google juice of this post by changing the title from "Bobs Math Answers" to something more accurate - this post isn't intended to be a Q&A for students who are having trouble with their math homework :)

     

Page 3 of 33 (815 items) 12345»