log

Shrimp

Mon Jan 29 18:02:18 EST 2007

For my birthday Wendy got me an EcoSphere with five lively shrimp. (There were more but Wendy loves sweet shrimp.)

Preserving their livelyhood has not been an easy task. When Jen bought an EcoSphere for Terence I told everyone it was a snowglobe. Karma caught up with me on my birthday when Saydra savagely grabbed my EcoSphere almost tsunamied my shrimp. Luckily her fast hands were no match for my fast mouth as I screamed, “NOOOOOOO!!!!!!!!”

I wanted to bring the shrimp to work so they could entertain me with shrimp tricks but I was worried all the crowding and rushing to transfer trains would stress the shrimp world too much. I decided to stuff the shrimp into a lunch bag I got for donating blood (which you should do if you haven’t done so already) to inconspicuously carry my five little ones. No one wants to steal or mess with a crappy lunch bag. I carried my bag to work by holding it by the strap, allowing the bag to swing freely to minimize inertial movement. To my fellow commuters, I looked like I was really afraid of my lunch. I also rotated the bag when making turns to prevent the branch from smacking my shrimp.

Eventually I got to work. I pulled out the whole package, inspected it to make sure the shrimp were still alive. They seemed very sleepy, probably from the cold. Now came the task of removing the EcoSphere from the packaging. This was not easy. The package was a tube with two circular caps; the top cap was sealed using tape, while the bottom was permanently bonded to the tube. Even after removing the tape, I could not remove the cap. Eventually I got it open by squeezing the tube to pop the top.

Now I have to remove the EcoSphere while trying to minimize movement. I could not just pull it out because my fingers could not slip between the EcoSphere and the packaging to get a firm grip. I could not remove the bottom cap and I had no scissors to cut the tube. The only thing I could do is to turn their world upside down and slide the EcoSphere out. I really didn’t want to do this, but it was the only way. So I slowly tilted the EcoSphere and gave the shrimp a rude awakening. Nothing as refreshing as exercise first thing in the morning!

I put the shrimp right in front of my keyboard at work so I could look to them for inspiration whenever I come upon a hard problem. Many times they congregate right in front of me, all five of them, as though they were having a meeting and spying on me. At one point I thought I had lost a shrimp; I saw three in the usually meeting spot, and one was out doing her daily chores. I thought the shrimp mafia forced their fifth member to swim with the fishes, but eventually I realized that it was just hiding itself behind another shrimp’s butt, giving the illusion of a very long shrimp.

I wonder why the shrimp gather near me. Is it because it is furthest away from my desktop, which may generate enough heat for them to feel? Is it because they are watching me watch them? Is it because they are stealing my passwords? I want to rotate their world but I think they’ve done enough travelling today.

I am so easily amused.

Reading Between the Lines

Thu Oct 26 15:43:03 EDT 2006

Today we’ll be talking about caveats when reading lines from a file.

cloder@ recently discovered a series of fgets(3) misuses:

	char buf[1024];

	fgets(buf, sizeof(buf), fp);
	/* Nuke newline. */
	buf[strlen(buf) - 1] = '\0';

fgets(3) includes a newline character from each line, but only if one was found. This is why most code truncates the string after calling fgets(3). There are three things wrong with this example:

  1. fgets(3) can fail and return NULL.
  2. buf[strlen(buf) - 1] might not be a newline, truncating a valid character. This can happen if the file did not end in a newline, if the buffer was too small to store the line, or
  3. buf might contain binary data and start with a NUL character, causing strlen(buf) to be zero. This, in turn, causes an out-of-bounds write.

The following example, copied directly from the man page, checks for all three errors:

	char buf[1024];

	if (fgets(buf, sizeof(buf), fp) != NULL) {
		if (buf[0] != '\0' && buf[strlen(buf) - 1] == '\n')
			buf[strlen(buf) - 1] = '\0';
	}

Note that lines containing NUL characters cannot be read reliably with fgets(3), since we are using strlen(3) to check the line length.

Some of these issues can be avoided by using fgetln(3). fgetln(3) allows for arbitrarily long lines and returns the number of characters read. Like fgets(3), newline characters must be removed manually; while this is easier to do with fgets(3) because the length is returned, a check must still be performed.

Here is an example, copied directly from the man page:

	char *buf, *lbuf;
	size_t len;

	lbuf = NULL;
	while ((buf = fgetln(fp, &len))) {
		if (buf[len - 1] == '\n')
			buf[len - 1] = '\0';
		else {
			/* EOF without EOL, copy and add the NUL */
			if ((lbuf = malloc(len + 1)) == NULL)
				err(1, NULL);
			memcpy(lbuf, buf, len);
			lbuf[len] = '\0';
			buf = lbuf;
		}
		printf("%s\n", buf);
	}
	free(lbuf);

A few things to note here:

  1. There is no strlen(3) call, so the only thing affected by NUL characters is printf(3).
  2. fgetln(3) cannot return successfully with a zero-length string, so the buffer cannot be accessed with a negative index.
  3. The free(3) is correctly placed, since it is only possible to call malloc(3) in the last iteration.

For a higher level of abstraction and more knobs, check out fparseln(3), which takes care of newline characters automatically and can recognize escape characters, comment characters, and continuation characters. Unfortunately, fparseln(3) has more overhead, is less portable, and each line must be freed afterwards. It is also impossible to check if the file ends in a newline; to reliably read every character in a file, use fgetln(3).

For more information, read the man pages: fgets(3), fgetln(3), and fparseln(3). Pay attention to the CAVEATS sections for fgets(3) and fgetln(3).

Multiplication Considered Harmful

Wed Apr 5 22:14:33 2006 (GMT)

Today I will be talking about proper string buffer handling with strlcpy(3), strlcat(3), and snprintf(3).

Many people now know to avoid the unbounded string functions (strcpy(3), strcat(3), and sprintf(3)), which are prone to buffer overflows:

	char newstr[9];
	const char *oldstr = "rm -rf /home/ray/tmp/*";

	/* Buffer overflow in all cases. */
	strcpy(newstr, oldstr);
	strcat(newstr, oldstr);
	sprintf(newstr, oldstr);

The bounded string functions take a buffer size argument and never write past that boundary, preventing buffer overflows. For arrays, the buffer size can be calculated with the sizeof operator. For malloc(3) allocated buffers, the size parameter given to malloc(3) can be used as the buffer size. The following examples use the sizeof idiom, but each sizeof instance can be replaced with the buffer size.

Note: the sizeof operator does not work with arrays passed as function arguments:

	char real_array[BUFSIZ];
	void
	function(char array[], char array2[BUFSIZ], chat *ptr)
	{
		/* sizeof(real_array) != sizeof(array) */
		/* sizeof(real_array) != sizeof(array2) */
		/* sizeof(real_array) != sizeof(ptr) */
	}
	int
	main(int argc, char *argv[])
	{
		function(real_array, real_array, real_array);
		return (0);
	}

In the example above, a pointer to the array is passed around, not the array. Thus the sizeof operator returns the pointer size instead of the array size.

The bounded variants of the previous three string functions are strlcpy(3), strlcat(3), and snprintf(3). These functions truncate the resulting string as necessary, preventing overflow:

	char newstr[9];
	const char *oldstr = "rm -rf /home/ray/tmp/*";

	/* Truncation in all cases. */
	strlcpy(newstr, oldstr, sizeof(newstr));
	strlcat(newstr, oldstr, sizeof(newstr));
	snprintf(newstr, sizeof(newstr), oldstr));

strncpy(3) and strncat(3) are also bounded functions, but they suffer from an unwieldy API. This is how to properly use strncpy(3) and strncat(3) to always create NUL-terminated strings and to never overflow:

	char newstr[9];
	const char *oldstr = "rm -rf /home/ray/tmp/*";

	/* Truncation in all cases. */
	strncpy(newstr, oldstr, sizeof(newstr) - 1);
	newstr[sizeof(newstr) - 1] = '\0';
	strncat(newstr, oldstr, sizeof(newstr) - 1 - strlen(newstr));

Compare the above to strlcpy(3) and strlcat(3). The strncpy(3) and strncat(3) functions are complicated, error-prone, and strongly discouraged.

Now buffer overflows are prevented, but a new problem arises: truncation. Undetected truncation can be deadly: do you really want to execute the truncated string produced above? To aid truncation detection, these functions return the resulting string length as if truncation did not occur. If this value is greater than or equal to the destination buffer size, truncation has occurred:

	char newstr[9];
	const char *oldstr = "rm -rf /home/ray/tmp/*";

	/* Detected truncation in all cases. */
	if (strlcpy(newstr, oldstr, sizeof(newstr)) >= sizeof(newstr))
		warnx("truncation");
	if (strlcat(newstr, oldstr, sizeof(newstr)) >= sizeof(newstr))
		warnx("truncation");
	if (snprintf(newstr, sizeof(newstr), oldstr) >= sizeof(newstr))
		warnx("truncation");

snprintf(3) comes from the printf(3) family and inherits all its quirks. Because of this inheritance snprintf(3) needs more than just truncation checks. For example, it interprets oldstr as a format string. Percentage signs (%) in the string can affect the result:

	char newstr[9];
	const char *oldstr = "100%off!!!";

	/* Format string error. */
	if (snprintf(newstr, sizeof(newstr), oldstr) >= sizeof(newstr))
		warnx("truncation");

To prevent this, always escape strings with %s. This rule applies to all printf(3) functions:

	char newstr[9];
	const char *oldstr = "100%off!!!";

	/* Format string error. */
	if (snprintf(newstr, sizeof(newstr), "%s", oldstr) >=
	    sizeof(newstr))
		warnx("truncation");

Additionally, the printf(3) family will return a negative value if there is an error and must be tested:

	int i;
	char newstr[9];
	const char *oldstr = "100%off!!!";

	/* Format string error. */
	i = snprintf(newstr, sizeof(newstr), "%s", oldstr);
	if (i < 0 || i >= sizeof(newstr))
		warnx("snprintf");

The above example demonstrates snprintf(3)’s last quirk: it takes a size_t for the buffer size but returns an int. This means that truncation detection involves comparing an int to a size_t. This can be avoided by using strlcpy(3) and strlcat(3), if your format string does nothing but string concatenation.

Here’s a recap of correct string usage:

	int i;
	char newstr[9];
	const char *oldstr = "100%off!!!";

	/* Detect all errors. */
	if (strlcpy(newstr, oldstr, sizeof(newstr)) >= sizeof(newstr))
		warnx("truncation");
	if (strlcat(newstr, oldstr, sizeof(newstr)) >= sizeof(newstr))
		warnx("truncation");
	i = snprintf(newstr, sizeof(newstr), "%s", oldstr);
	if (i < 0 || i >= sizeof(newstr))
		warnx("snprintf");

I hope that this has been helpful, but before submitting any patches please be sure they are correct and solve actual problems. Thanks!

Multiplication Considered Harmful

Thu Mar 30 07:19:48 2006 (GMT)

Today we’ll be talking about the dangers of multiplication integer overflows in C, the type of bug responsible for the OpenSSH hole of 2002.

Integer overflows occur when an operation causes a number to grow or shrink out of bounds, resulting in a much smaller or much larger number than expected. For example, a char can only hold 256 values. If two chars are multiplied together, there is a possibility of overflow:

	char a, b;

	a = b = 127;
	a = a * b;	/* overflow */

Because arithmetic is done in code all over the place, adding checks can be very tedious and is often ignored. A common idiom to allocate memory is:

	size_t num, size;
	char *ptr;
	…
	if ((ptr = malloc(num * size)) == NULL)	/* overflow */
		return (NULL);

The multiplication can create a number larger than a size_t can hold, causing an integer overflow. The integer overflow in this case causes less memory to be allocated than the program expects. The program could then take user input and write past the actual small allocation into memory it thinks it owns, causing problems like a generic buffer overflow. This was the idiom used in the OpenSSH hole of 2002, demonstrating the severity of these bugs.

We recommend using calloc(3) whenever allocating multiple objects of the same size. The above code would be written as:

	size_t num, size;
	char *ptr;
	…
	if ((ptr = calloc(num, size)) == NULL)
		return (NULL);

calloc(3) has checks to make sure the multiplication does not overflow.

Unfortunately there is no safe replacement for realloc(3). To prevent multiplication overflow, a check must be added:

	size_t newsize, num, size;
	char *newptr, *ptr;
	…
	if (num && size && SIZE_MAX / num < size) {
		errno = ENOMEM;
		return (NULL);
	}
	newsize = num * size;
	if ((newptr = realloc(ptr, newsize)) == NULL)
		return (NULL);
	ptr = newptr;

This is a hassle, but it is important to add these safeguards, especially when doing multiplication, which can quickly overflow.

To lessen developer work and simplify code, an xrealloc() function was added to ssh. xrealloc() has a similar API to calloc(3), taking both a num parameter and a size parameter. The above example can be written using xrealloc() as:

	size_t num, size;
	char *newptr, *ptr;
	…
	if ((newptr = xrealloc(ptr, num, size)) == NULL)
		return (NULL);
	ptr = newptr;

Due to its elegance, this API was also imported for OpenCVS.

For more information, please read a -current malloc(3) man page, which was recently updated with better usage examples.

Sat Mar 11 02:57:45 EST 2006

I am now an OpenBSD developer. It’s interesting how in less than four months I went from being a guy writing his first large (1000+ lines) C program to an OpenBSD developer, a status which I highly respect.

Here’s the sequence of events that occurred.

In 2004, I started submitting diffs for various stuff I noticed in man pages. jmc@ is very responsive about these things, he’s awesome. But these were mainly minor things (grammar, spelling, et cetera) and weren’t really what I wanted to do, which was to code.

I knew some C, I’ve had lots of accumulated experience with various C-like languages (C, C++, Java, PHP, Perl, in that order). In school I never actually wrote anything big with C, so while I knew the general syntax, I didn’t know any of the trickier things (such as pointers, as otto@ can attest) nor did I have experience with any of the standard libraries.

I knew I needed to learn C to code for OpenBSD, so I set myself to learn C. I read, cover to cover, K&R. I submitted simple diffs to tech@, mostly to do code cleanups (style(9) and -Wall sweeps). Cleaning old code forced me to understand the code I was cleaning up, to make sure I didn’t introduce any bugs. In the process, I found various old bugs lurking.

This was still not enough, though. I needed to demonstrate that I could code, not just be a lint(1) brush. After all, a developer means develops code. I also needed experience in actual code, learning the intricacies of C. I needed to develop a project, one that could be committed to OpenBSD. I then thought of writing sdiff(1).

sdiff(1) was originally a part of OpenBSD, until the GNU diff utilities were replaced by nicer licensed ones. diff(1), diff3(1), and patch(1) were all replaced but not sdiff(1). I disliked having to install the GNU diff utilities just to use mergemaster from ports. Diff output looked relatively easy to parse, just some line numbers here and there, lines preceeded by +, -, <, or >. Certainly doable, with enough effort.

I set out to write sdiff(1). To make life easier for myself I looked for the simplest diff output to parse. I initially wrote sdiff to parse diff -n output. Its output looks similar to ed lines, d# #, a# #. I had a version that worked well using diff’s -n flag, but I wanted the code to be portable across different diffs. This required a major rewrite to parse the default diff output, which is standardized.

I was very reluctant to do so, but there came a point when I just didn’t have anything else to do with sdiff, and I knew that I needed to do this eventually. It would be good experience anyway. So I rewrote major parts of sdiff and wrote lots of regression tests along the way, making sure that sdiff still worked with every change.

Eventually I got so sick of doing sdiff that I didn’t want to nitpick little things anymore. It seemed to be working fine, I ran it several times through mergemaster. So I mailed it to misc@, seeking reviews or comments.

A couple of days later I got some complaints from deraadt@ about my poor coding style. I fixed the points he brought up and a few days later tedu@ committed my code. I was happy.

Developing sdiff was a very helpful experience. As I was struggling to master C, I looked through countless man pages and code examples in /usr/src to see how other developers implemented the action I wanted to take. This revealed to me bugs in the manuals, userland utilities, and even the libraries, many of which I submitted fixes for.

Eventually I was contacted separately by marco@ and otto@, both inviting me to different chat rooms to get to know me better. Several more fixes later, I was offered an account.

If anyone wants to be a developer, start looking for things to do. Look through old crusty code nobody cares about, those generally are poorly maintained and can be cleaned up. Cleaning up code forces you to understand the code to make sure you don’t mess it up along the way. Understanding the code reveals bugs. If you don’t understand something, look in the man pages. If the man pages are lacking, submit diffs to improve the manuals. If you need examples on how to use certain functions or system calls, look through the source tree. They usually provide great examples. When you see all the different ways functions like fgetln(3) are used, you start seeing more bugs. That was how I came across the cgetnext(3) and cap_mkdb(1) bugs. Repeating these steps just dropped me deeper in the rabbit hole.

There are certain essential skills to have. You need to learn to use CVS. All code is kept in CVS, diffs are easy to create (diff -upN), and you can keep track of your own changes in your projects. You need to learn to submit good sendbug reports. It’s good to report bugs, but it’s better to submit a fix. It’s much easier to look at a fix and verify that it works than to come up with a fix for a problem that wasn’t encountered by myself. Learn to use GDB. Core dumps are suddenly not just a waste of space. You can use backtraces to figure out where the NULL pointer dereference was, or the double free(), or buffer overflow. It’s much nicer than using printf(3) all over the place.

Good luck!

Mon Feb 27 01:04:47 EST 2006

I’m worried about Sharon. I wish she would find a roommate already. Good luck.

I’ve been invited to do more OpenCVS/OpenRCS development. By “invited” I mean they invited me to join their SILC channel and they sent me their OpenRCS todo list. Unfortunately niallo either forgot to attach the list or everything’s done and he just sent me the empty list. I’m guessing the former and am waiting for him to get back to me about that.

All this OpenBSD development has halted my work on my Systrace talk. Although I’m quite confident when it comes to most things Systrace, I really should prepare so I don’t look like a fool at NYCBUG and risk losing that speck of respect I’ve worked so hard for.

I’m sick of typing HTML. Give me my markdown.

Sun Feb 26 11:29:55 EST 2006

Continuing to find bugs here and there, some obviously need to be committed quickly, others might go in after 3.9, since the tree lock is coming.

I’ve always wanted to play with OpenCVS, but its reliability worried me. Since it worked with the CVS repository I didn’t want it to trash all my revisions accidentally. I didn’t mind losing my current work, but to lose the entire history would be unacceptable. Since I’ve been working with OpenBSD’s stuff recently I can now play with OpenCVS with more ease. It’s not like I have an account on the anoncvs servers, so the most I’ll do is wreck that small diff that I was just working on.

Playing with it has shown that there are still a lot of rough edges. It doesn’t ignore changed $OpenBSD$ tags, for instance, and it also thinks that any file that has a different mtime from the one stored in CVS is modified. I’ve been working on doing what I think GNU CVS does, which is to do a file comparison in addition to checking the mtime. If I work on it for another day or two it should be done.

I’ve been wanting to automate this log for some time now, but I didn’t want to do it in php, which would have been easier. I also wanted to use John Gruber’s MarkDown. I’ve been thinking of doing a reimplementation in C, libmarkdown, so it can be linked in to other CGIs or just executed and parse stdin. My motivation to do this will not come until I at least get this log automated.

I need more time. I need to prepare for the Systrace talk that I’m giving on March 1st, I need to finish up other miscelleneous things I’ve been putting off. So much for being master of time.

Tue Feb 21 02:59:32 EST 2006

I’ve been doing a lot of OpenBSD development recently. (Bug hunting is more like it.) Because of my "1337" skills I was invited to join the OpenBSD community somewhat. While I wasn’t invited to become a fully fledged committer, I’m now hanging out in a channel with many active developers. It’s interesting, seeing the people I’ve always read about from a distance talking amongst themselves in real time.

So recently I’ve been working out all those little things that nagged me about sdiff, such as the -I flag not working if the ignored diffs included line changes and the fact that sdiff didn’t work well with stdin. The -I flag bug was there because of the way I originally implemented sdiff, reading only the first file and the diff for all sdiff output. At the time I figured, the less files and pipes I had open, the less resources I had to use and the more efficient it would be. This proved to be a problem when with the -I flag, because sdiff parses line numbers from the diff output, and the -I flag can mess up the line numbers by ignoring diffs consisting of line additions or deletions. Especially for line additions, since by not reading file2, sdiff wouldn’t even know what to print out.

The fix, naturally, was to read both file1 and file2. Then instead of assuming that after doing a diff the line numbers will always be aligned and continue reading file1 and file2 until the line numbers are synched up.

The stdin bug had eluded me for a while. At first I thought that fparseln doesn’t work with stdin, but writing a simple program dispelled that theory pretty quickly. Since I suck at gdb, I used this opportunity to learn to use it. Looking through the backtrace and figuring out where it stopped and why, I finally realized that when I pass stdin to sdiff, it also gets passed to diff. Correct operation would require both sdiff and diff to read the file and have the same output. Since stdin is a one-time thing, the two processes essentially fought over the stream and both lost. The fix I implemented was to copy all inputs to temporary files and pass them to diff and sdiff to use. If file1 and file2 are the same name only one temporary file is used. I don’t like this because it requires $TMPDIR to contain enough space to hold both file1 and file2, but I don’t see a simpler solution. Additionally simply the two filenames may not mean that they are not the same file. I haven’t tested out what would happen if I tried to do "echo a|sdiff - /dev/stdin", but I don’t think it will be pretty. I also wonder if open("/dev/stdin") == STDIN_FILENO; if so, that would be handy and I could compare those as well.

While looking for examples of correct fgetln, ferror, and feof usage, I stumbled upon some bad code in /usr/src/usr.bin/cap_mkdb. I’ve discovered that there was a lot of code in there that has overflows, NULL dereferences, and other autrocities. Fixing them first required me understanding the input formats. I think I’ve pretty much figured the file formats out. One odd thing was, all the comments had the function names spelled incorrectly. I had a nagging feeling the code was copied and pasted from somewhere, but a cursory search revealed nothing.

So I created some junk inputs that caused segfaults, created regression tests out of them, and submitted fixes via sendbug. Continuing to create more junk inputs and using gdb to trace the source revealed the source of the copy and pasted code, somewhere in libc! Looking into it, I realized that almost every bug in cap_mkdb was copied from the libc code. Ugh, now fixing it will be twice the work.

The more I learn, the more problems I find. I guess this is good, finding things to fix, improving the overall quality of the system. I can see how there’s always room for improvement and areas to fix, though. I can see how the goal of creating robust, bug-free code will be a very long one as well. And I’m just doing housekeeping in dusty code nobody cares about. I hope that this is not all that I wind up doing, though, cleaning up crappy code in obscure areas. I do want to do development.

Tue Jan 24 00:40:37 EST 2006

Tonight I was driving Maria and Jen home. Maria had moved to a new house and my brother usually took the bus there, so nobody really knew how to drive to her house. We took the Grand Central Parkway to her exit and my brother kept telling me to turn at the last minute and getting his lefts confused with his other left.

Once she was dropped off, my brother announced that he didn’t know how to get home from here. We drove around and tried to retrace our steps and eventually found our way back to the Grand Central Parkway.

Since we took the Grand Central Parkway to Maria’s place, we took it back. Going to Jen’s place I usually take the Van Wyck, but we could also take the Jackie Robinson, which was more convenient from the Grand Central, so we took it. Unfortunately I hadn’t taken the Jackie Robinson in a while, and neither had Jen.

I thought we were supposed to get off at the Metropolitan Avenue exit, but she insisted that we were supposed to get off at Union Turnpike. Two exits after the Metropolitan Avenue exit, we realized that we had gone too far. Jen calls her mom who confirms that we were supposed to get out at the Metropolitan Avenue exit.

So I get out at the next exit and look for the entrance to the Jackie Robinson going the other way. We couldn’t really find anything, except that we were surrounded by graves on all sides, and when we finally got on to local roads, we were in the scary streets of Cypress Hills. We quickly doubled back onto the graveyard road and found our way back onto Jackie Robinson West.

Once on there, I realized that we were supposed to be going East. So we exited at the next exit again and this time found the East entrance much quicker and were on our way to Jen’s.

We arrive at Jen’s, exhausted. I drop her off and I drive home.

As we were dropping off Maria we had passed by a place called “Nice Ices”, where I picked up an Italian ice craving. So as I was driving my brother back, I asked him if he wanted to go for some Slurpees, the closest thing to an Italian ice we could get at this time.

We arrive outside the 7-Eleven and I parked next to a puddle of water. My brother opens the door, I hear a “plop”, and my brother goes, “Oh, shit.” I walk around the car to see what dropped in the water, but all we see is a single woman’s shoe. We debated a bit about whether it was the shoe or something else that dropped in the water and if we should pick it up. We didn’t want to leave the shoe if it was ours, but we definitely didn’t want to pick up a random shoe out of a puddle and bring it home. Finally I realized that if this shoe came out of our car, there should be a matching shoe in the car, and told my brother to look in the car for it. Unfortunately, he found it, so we had to pick it up. Neither one of us wanted to touch it, so he found some napkins and picked up the shoe, shook some of the water out of it, and tossed it in the back seat.

We finally went into the 7-Eleven and got our Slurpees. My brother also a tacquito or something. We went to pay for the food and the guy charged me four something. I paid for it, but then he pointed to my brother’s tacquito and said that he didn’t charge me for that. I thought, “Oh, how generous!” I turned to leave but my brother stood his ground. I asked him what was wrong, and he told me that the guy didn’t charge for the tacquito. I said, “yeah, so?” Then it dawned on me that he wasn’t actually giving it to me for free, he just made a slip-up and wanted me to pay for it.

What a night.

Fri Jan 13 18:13:52 EST 2006

Today I went to ShmooCon. I packed and slept over at Wendy’s, I set the alarm and at 7:20 as I heard the alarm I thought, “Oh shit, I’m supposed to leave at 7:20, not start to get ready at that time.” I left the place at 7:30 a.m., got to the bus at at 7:45 a.m. or so. A lady near where the bus was supposed to be asked me where I was going, I told her D.C., she asked me for my ticket, I showed it to her, directed me to the Washington D.C. bus, I got on. An Indian passenger asked me where I was going, I let him see my ticket. He told me he was going to a different place, I said, “Well, it’s still D.C.”

Later the bus driver came to collect tickets. I showed the ticket, he said that this ticket was for another company’s bus, the bus behind this one.

But that bus has already left. Do I want to take this one to D.C.? I’s have to buy a ticket.

Stuck with the prospect of either being an hour or more late or taking this bus, I chose to take this bus.

Once I paid and the bus started, the Indian guy said, “Hey Ray, I told you this was going somewhere else.”

Tue Jan 10 01:05:09 EST 2006

I wrote sdiff; on Tue Dec 27 04:04:56 2005 UTC it fulfilled its destiny.

I wrote livewipe; then I discovered secure delete.

Lots of commits.

I am getting better at C.

Jen scolded me for using printfs.

Mon Jan 2 02:03:31 EST 2006

“Champagne for my real friends,
Real pain for my sham friends.”

Wed Dec 28 08:26:01 EST 2005

Me: Do you think my screen name is offensive?
Will: If one has no sense of humour.

Sun Dec 4 05:38:56 EST 2005

This weekend we had a potluck as a late Thanksgiving among friends. Some people were not invited because some others held grudges against them; my girlfriend, unaware, was one of them.

The organizer and I discussed this problem over dim sum earlier. We were not sure who to invite. Though I did not want to reward the grudge holders, they were hard to meet up with and we have not seen them in a long time.

I upset my girlfriend and lied that my friends wanted to see me alone; to protect the grudge holders I tarnished my own image. The grudge holders wound up canceling (for other reasons).

Happy Thanksgiving.

Thu Dec 1 21:00:30 EST 2005

Last night was the company holiday party. I danced with a bunch of my male and female coworkers. One guy, while sitting, repeatedly grabbed this girl’s butt as she was dancing. It’s good she didn’t notice.

Wed Nov 30 02:39:35 EST 2005

I have been sleeping progressively later from the moment I decided to improve my time management. Tonight I cleared up the mess at my bank. At least it was constructive.

Mon Nov 28 10:21:14 EST 2005

At work we have a microwave on top of the water dispenser. My head feels warm whenever I bend down to get water while someone is nuking some food. I worry that the microwave leaks more than heat.

New Year’s Resolution

Sun Nov 27 23:34:43 EST 2005

I will master time.