XScreenSaver 5.00a13

Come and get it:

There is a memory corruption bug in image loading that I have been beating my head against for days, and have so far been unable to track down. If you have a Mac development environment, please help me figure out where it's going wrong...

    Some of the image-displaying savers will run for a little while, but most of them blow up immediately in some nonsensical place. Maybe the autorelease pool is getting corrupted? I've tried everything I know (MallocDebug, the various $Malloc* env. vars), and I've gotten nowhere.

    It probably has something to do with waiting for the result of a pipe, because the problem goes away if the "#if 0" is changed to "#if 1" in grabclient.c: osx_load_image_file_async() to make all images load synchronously instead of asynchronously. Perhaps this implicates jwxyz-timers.m, though I've stared at that code quite a bit.

    Update: I think this is fixed now. Thanks, zetawoof!

Other things that I'm still stuck on, and that I still haven't gotten any happy LazyWeb action on:

  • Why do none of my .saver bundles show up with a custom icon in the Finder, even though CFBundleIconFile=XScreenSaver in Contents/Info.plist, and Contents/Resources/XScreenSaver.icns exists?

  • Why is this getting logged every time you hit OK in the preferences dialog:
    "SaverTester[]: ScreenSaverDefaults does not respond to volatileDomainForName:"

  • If you delete all the text in a preferences text field (e.g., in sonar), this happens after hitting OK:
    NSRunLoop ignoring exception '-[NSCFDictionary setObject:forKey:]: attempt to insert nil value' that raised during posting of delayed perform with target 3d9ea0 and selector 'invokeWithTarget:'

  • Assertion failures seem to result in the program just hanging (in the event loop, with the screen perma-blanked). How do I make them cause the program to exit instead?

  • The image-generation preferences aren't displaying quite right; the radio-buttons don't come up selected by default, and the labels are wrong. Basically, I can't figure out how to use NSMatrix properly. (Code is in OSX/XScreenSaverConfigSheet.m: make_text_controls().)

  • The "Text File", "URL", and "Choose Random Image" fields in preferences should probably be spin-boxes that remember all previous entries into those fields in any of the screen savers. I don't know how to do either part of that.

  • When the "Image Directory" preference is long, the field displays the first part of the string; it should scroll to display the last part instead. I tried a few things, but I can't figure out how to do that.

Tags: , , , ,

19 Responses:

  1. duskwuff says:

    The one thing I'd wonder about image loading is whether you're performing Cocoa API calls in a secondary thread. Cocoa is - unless stated otherwise - not thread-safe; if you want to do API calls, you do them with -[NSObject performInMainThread:].

    • jwz says:

      There are no threads. I run a perl script at the other end of popen(), then select() for it, wait for it to print a single line to its stdout, pclose() it, and then load the file name it printed, right there in the main thread.

      Really, I've stared at this for enough days that "did you think of" suggestions aren't going to go anywhere. I need someone who (unlike me) is actually able to get any usable information out of Apple's asstastic memory-debugging tools to actually do so, and point me at a damned line number.

      • duskwuff says:

        OK, never mind then. On to the code.

        Grabbed the source and saw something in osx_load_image_file_async, though, that might actually have memory-management implications.

        utils/grabclient.c:625: struct pipe_closure *clo2 = (struct pipe_closure *) calloc (1, sizeof(clo2));

        Shouldn't that be calloc(1, sizeof(*clo2))? Otherwise you're allocating an undersized block, which could cause all sorts of havoc...

        • jwz says:

          You rock! I think that was it. Thank you!

          So, the next question is, how could I have found this, other than by eyeballing the code until I saw the one-char typo? Given that none of MallocScribble, MallocPreScribble, MallocGuardEdges, and MallocCheckHeapEach said anything, I don't know what else I could have tried.

          • duskwuff says:

            MallocGuardEdges would have caught the problem if the block being allocated were "large". However, the block you're allocating here is "small" (four bytes, as written), so that option doesn't get used. (Otherwise the allocator would have to map five pages for every allocated block, which would get very messy very quickly.) So I'm afraid that there's unfortunately no good answer.

            (I don't know what exactly counts as "large" to the allocator; man malloc doesn't go into details. I'd assume that anything above page size - 4KB - is probably considered "large", but I wouldn't count on it.)

            Best I can come up with is "don't use that syntax; use sizeof(type) or a macro instead". Not really a solution, I understand, but it's the best I've got.

            With regard to the issue of assertions not working properly... you're going to have to be creative here. Savers are not supposed to terminate the application that loaded them (System Preferences or ScreenSaverEngine); as such, calling abort() or the like may leave the display in a seriously inconsistent state. My suggestion here would be to throw an Objective-C exception and have a handler in -[ScreenSaverView drawRect:] that catches such exceptions and forces the module into a fail-safe mode.

            • jwz says:

              Purify had an option to do exactly that: allocate N pages for every call to malloc. It slowed things down a lot, but it worked.

              I think the problem with assertions is exactly the opposite of what you're saying: if my code calls abort() or exit(), then the screensaver driver is re-launched, the screen flickers, and everything's basically fine. (I mean, obviously you don't want the saver to abort at all, but at least it doesn't hose the machine.) The problem is that assertion failures do not call abort: they do something else -- probably throwing an exception -- which seems to cause the screen saver driver to remain running but go catatonic and refuse to ever un-blank. So I guess I need to catch them myself and then abort()?

              • duskwuff says:

                'Fraid you're on your own with the assertion trouble, then. I don't use them much myself, so I don't know what it's doing.

                Sanity check: Are you aware of screen saver debug mode? If you launch the ScreenSaverEngine executable yourself with the -debug option set, the saver will run on the desktop (as if you'd used -root on an xscreensaver hack) instead of grabbing the whole screen, allowing you to attach GDB and see what's going on. Make sure password protection is turned off, though; weird things happen if it's on.

  2. darkshadow2 says:

    Unfortunately, OS X doesn't show custom icons in bundles other than application bundles. Any bundle you see that shows something other than a folder icon is because some application, somewhere, is claiming that bundle as its own and has an icon associated with it.

    In the case of .saver bundles, System Preferences is claiming them (you can look at its Info.plist file to see that).

    While you can't add an icns file to a bundle and have it get shown, you are able to paste an icon on the bundle. It's a pain in the butt, but it's the only way you're going to get them to have a custom icon. Is it really all that important, though? Most people probably don't go around digging in their Screen Savers folder, so it's not likely the icons would be seen all that often, anyway.

    • legolas says:

      What effect does pasting an icon have exactly? Does this go into a .DS_Store, some resource fork of some file in the bundle, or something else still? (In fact: does it go into the bundle at all, or is it kept on some large db somewhere, and if so, does it move with the bundle to another folder/another system?)

      • darkshadow2 says:

        It goes into the resource fork of a file named Icon\r (where that's an actual return character in the name) in the folder, or into the resource fork of a file. For a folder, its Finder flags are also changed to show that it has a custom icon.

        Since a bundle is a folder, it works that way for them. Since the icon is in an actual file, it moves wherever the bundle is moved. The only problem you'd run into is moving the bundle onto some file system that doesn't work with resources, or compressing it with something that isn't resource fork aware (in those cases, you'd likely lose the Finder flags on the folder as well).

    • jwz says:

      It's not that important, mostly I was just curious why it didn't work, since the documentation seemed to say it should.

      The place people will see these, mostly, is in the .dmg. Though I was hoping the icon would also show up in the list of screen savers. (Some of Apple's savers have different icons there.)

      I keep seeing people say that you can paste an icon into the Get Info window directly, but I can't see how -- there don't seem to be any drop targets in there, and Paste is grayed out. How do you do that?

      And is there a way to do it from AppleScript?

      • darkshadow2 says:

        Just to make sure, you have to click on the icon in the Get Info panel first before you can paste anything. But if that isn't it, probably the Finder doesn't like whatever format the picture you're trying to paste is in. It's sort of hit and miss unless you're copying an icon from one file and pasting it on another.

        There use to be a way of using Applescript to paste icons, but it doesn't seem to work anymore. I was just trying out an old script of mine to see if it still worked.

        • jwz says:

          Oh, I see, you have to select the little icon at the top, not the big one in the "Preview" at the bottom. It's weird, it works when I copy my icon from "SaverTester.app" to "Anemone.saver", but if I try to copy the actual .icns file, or a .tiff extracted from the .icns, it loses the image's alpha and displays it on black!

          • wootest says:

            Not if you hit Cmd+N to make a new image from clipboard in Preview. Then you can resave it as a PNG, which seems to be the only thing that doesn't totally jive the image's alpha; at least not in Photoshop, and the same thing could be true for the GIMP. (It's been too long since I used OS X X11 GIMP so I couldn't tell you.)

            • wootest says:

              Er, and by Preview, I mean the app Preview, not the springy section in Get Info. Man, Apple name their droll apps to colloquially.

        • jwz says:

          Some googling suggests this should work, but it doesn't (compiles, but fails getting the icon):

            tell application "Finder"
                  set new_icon to icon of file ".../SaverTester.app"
                  set icon of file ".../Anemone.saver" to new_icon
            end tell

  3. nealsid says:

    what is the point of "DND" if anyone can read your LJ to get the binaries?

    • jwz says:

      "Do not distribute" is code for "Hey Linux distros, don't go pushing this out to a zillion people just because you stumbled across a tarball."