Dilbert





The Daily WTF

Error'd: Nobody is Perfect;

"Google notified me that it needs help from a human and then displayed me this image," Jeff K. wrote, "I think I may need some help too."

 

"I'm really glad that Pizza Hut's batch job finished up...does this mean I get one of those 16,867,183 coupons?" Lincoln K. wrote.

 

Stefan Z. writes, "Testing is important for any business' site. Even if, sometimes, it's in production."

 

"I give you The Sam's Club business model:

  1. Drop password length limit from 25 to 12
  2. Apply new validation to existing passwords
  3. ???
  4. Profit!" George writes.

 

"My feedback for World of Tanks is as follows - 'I had a VERY negative experience'," writes Piotr.

 

James P. writes, "So...which one do I install first?"

 

[Advertisement] Otter - Provision your servers automatically without ever needing to log-in to a command prompt. Get started today!


CodeSOD: Getting to YES;

“We’re a dynamic, multi-paradigm organization, and we do most of our new development in a blend of Ruby and Go. We’re not the kind of company that is dogmatic about tools though, we just want to deliver the best product for our customers.”

That’s what Delphia was told in the interview. She didn’t quite grasp why they were mixing those two techs in the first place, but the interview went well, and she took the job. It was then that she discovered that everything she’d been told was technically true.

The company had just finished a sprint where they tried to pivot and reimplement their 15 year old codebase in Rails and Go. At the same time. It was difficult to tell from the code if this were a case where they did both together, or they were parallel projects, or frankly, if there was any coordination between either of them. That was their new development.

The company didn’t do much new development, though. The core of the company’s business was a 7,500 line PHP file which contained a single form. It depends on includes from dozens of other files, some of which depend on functions defined in the main PHP file, and thus can’t be used in any other context but that include. It’s less spaghetti code or even a big ball of mud, and more a career killing meteorite.

But it makes more money for the company in a day than Delphia can rightly count.

One function that Delphia kept seeing invoked a bunch was yesorno. It was used almost everywhere, but she had never seen the definition. So, curious, she went digging. And digging. And digging. Someplace around the fifteenth or thirtieth include file she read through, she found it.

function yesorno($in) {
  if($in == 'YES') return 'YES';
  else             return 'NO';
}

That indentation, by the way, is typical of about 30% of the codebase, maybe less. You wouldn’t expect any sort of consistency in this kind of code, would you?

[Advertisement] ProGet supports your applications, Docker containers, and third-party packages, allowing you to enforce quality standards across all components. Download and see how!


A Backup Pipeline;

“Um… can you come take a look at something for me?”

Pat looked up from happily hacking away at some new features to see Milton hovering at the edge of the cubicle.

“I think I messed up,” Milton added.

One of their company’s main internal systems was a data processing pipeline. “Pipeline” might be a bit generous, as in practice, it was actually a webwork of shell scripts and Python that pulled data in from files, did “stuff” to that data, and dropped the results into other files, which could then be read in by different scripts. In actual use, this usually meant people grabbed the latest version of the scripts from source control, modified and tweaked how they executed to answer one specific data-related question, and if that particular process seemed like it might have value, they’d clean the code up a bit and then try and merge it back up into source control. Otherwise, if they didn’t think they’d need it again, they’d just reset back to HEAD.

Some folks, though, like Milton, mostly kept their own copy of all the scripts. Or in Milton’s case, multiple copies. Milton knew the data processing pipeline better than anyone, but the vast majority of that knowledge was locked up in his personal scripting library.

“I was thinking I should probably try and contribute changes back upstream,” Milton said. “So, like, I’ve got a script that’s called by a script, which is called by a script, and it depends on having a bunch of shell variables created, like $SETUP_DIR.”

Pat nodded along.

“So I wanted to refactor that into an argument, so other people could use it. And I did… but I forgot to change the calling scripts to pass the argument before I tried to test it.”

Specifically, Milton’s script had a line like this:

#!/bin/sh

rm -rf $SETUP_DIR/*/

Which he refactored into a line like this:

#!/bin/sh

rm -rf $1/*/

Shell scripts don’t care if these variables exist or not. Milton had an environment which always guaranteed $SETUP_DIR existed. But $1 is the first argument, and if you don’t pass an argument, it’s nothing. So Milton’s new script, when executed with no arguments, expanded to rm -rf /*/- deleting everything his account had access to.

Mostly that meant lots of failed attempts to delete files he didn’t have the rights to. It also meant his home directory went away, along with his entire packrat pile of spaghettified scripts that were absolutely impossible to reconstruct, as they’d never been placed in source control.

“There’s a way to fix this, right?” Milton asked.

“I mean, sure. You can restore from the last backup you took,” Pat said.

While all the Windows boxes were running an automated backup tool, installed automatically, none of the Linux boxes were so configured. The support team took the stance that if you were technical enough to be running Linux and writing shell scripts, you were technical enough to set up your own backup solution. There was a SAN available to everyone for exactly that purpose.

“Oh, I… never set up a backup,” Milton whispered. “Well… at least I didn’t push?”

Pat wondered if Milton was learning the right lesson from this mistake.

[Advertisement] Ensure your software is built only once and then deployed consistently across environments, by packaging your applications and components. Learn how today!


CodeSOD: A Knotted String;

Rita caught a weird bug. It was weird, in part, because there hadn’t been any code changes in their label printing application for ages. Yet, there was a sudden new bug. Labels were printing with what was obviously unicode garbage. Interestingly, the application definitely supported unicode- there had been a huge effort a few years back to move the C++ code from chars to wchars.

Rita started debugging, and confirmed that when the label text was populated, memory stored correct values. By the time the data was printed, it no longer did. Obviously, there was something wrong with memory management- something was touching the end of the string and throwing off the output. That was an easy enough bug to make in C++, but tracing through 7,000 lines of label printing code to figure out where things got thrown off was more of a chore, especially with the… “friendly” variable naming convention the original developer had used.


  wchar_t* xyz = new wchar_t(wcslen(abc));

That doesn’t look like much, does it? xyz is a pointer to a wchar_t, a pretty typical way to represent a string, and then we initialize it to point to a new wchar_t with a size equivalent to the length of an input string, right?

Of course not. There’s an important difference between new wchar_t(10) and new wchar_t[10]. The latter makes a block of memory capable of holding ten wide characters. The former makes a single wide character and initializes it with the value 10. So xyz points to a single character, but this is C++. If you disable enough warnings, there’s nothing that will stop you from abusing that pointer and running off the end of memory.

The real WTF though, is that this wasn’t a bug introduced back when they switched to wchar. Going back through the source control history, Rita found the previous version:


char* xyz = new char(strlen(abc));

It had the same bug. The bug had gone undetected for all this time because the message string had been short enough that nothing else was touching that section of memory. It was only after a user had recently tried to add a slightly longer message that the buffer overrun started causing problems.

[Advertisement] Forget logs. Next time you're struggling to replicate error, crash and performance issues in your apps - Think Raygun! Installs in minutes. Learn more.


CodeSOD: The Double Bind Printer;

Printer jams are an unavoidable circumstance. Paper might not seem sticky, but static electricity and humidity can to horrible things, and all the protections in the world can't stop them. Printers are also objectively terrible, and always have been.

Years ago, when Coyne T was a fresh-faced youth, he supported an aging COBOL-based warehouse system. Their reports went to a noisy continuous feed line printer. The printer was a finicky beast, and no matter what sacrifices were made before printing, it had a tendency to devour pages, or have the gears slip and misprint lines. Because the report data came straight from a COBOL job, there was no cached output to reprint- the entire reporting process would need to be re-run every time the printer got angry.

About thirty pages of a report got chewed up a few days after Coyne joined the team. As the new kid, re-running the report fell to him. "But, before you go and re-run the report," the team lead, Herschel, said, "there's a few things you should know."

Herschel laid out the key facts. The report generating program didn't just generate the report. It also could clear the monthly totals and update the day's inventory as part of running the report. Doing either of those in the middle of the day was absolutely wrong, and would create a lot of manual work to fix it. Even worse, doing both of those during the same run would stomp all over the data files and absolutely corrupt the entire mainframe.

"Fortunately, I added two flags- CLEAR-SWITCH and UPDATE-SWITCH. Just make sure they're both set to 'N', and you should be fine."

Coyne checked and double checked the instructions, ensured that the flags were definitely set to 'N', and then ran the report.

After the mainframe team had restored from backup and recovered as much of the data that they could (losing only the past three days worth of inventory), Coyne was called out on the carpet to explain how he'd destroyed the database. Despite being a newbie, Coyne had been cautious from the start, and reviewed the instructions which Herschel had given him, as well as how he'd verified that he'd followed the instructions. But Coyne had also gone the extra step, to look up the code block which checked those flags. He came to the meeting prepared.

IF NOT CLEAR-SWITCH = 'Y' SET RUN-UPDATE TO TRUE END-IF IF NOT UPDATE-SWITCH = 'Y' SET RUN-CLEAR TO TRUE END-IF

Now, first off, one might hope that if two flags should never be used at the same time lest they destroy all the data, there might be some logic to make them mutually exclusive. There is no such logic, but if you look closely, you can see some illogic that I suspect was meant to accomplish that goal.

If the CLEAR-SWITCH variable is not 'Y', then the code sets… RUN-UPDATE. And RUN-CLEAR is controlled by UPDATE-SWITCH. The result is an odd block of code: if either one of the switches is disabled, by being set to anything that isn't 'Y', this code works. If both of the switches are enabled, by being set to 'Y', then this code won't run either step. In the case where both switches are explicitly disabled, then both steps will be run (and the data will be destroyed).

Herschel warned Coyne that "only an idiot would run a job without reading the code first, you should have been more careful," but since Coyne was only a junior, and had followed the instructions, nothing more came from that meeting.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!


Error'd: Quite Powerful Errors Indeed;

"I was considering this recorder for my son, but I think 120W might be a bit over the top," writes Peter B.

 

"Yep, looks like the test works to me," writes Stefan S.

 

Lincoln K. wrote, "Hertz may have Ultimate Choice(tm) but apparently not Ultimate QA."

 

"Consider a situation where seven electric vehicle chargers were installed and they were working ok, but monitoring wasn't working so great. After two weeks of finger pointing and 'not my fault' emails, an agreement was reached - inspect the terminal sockets and replace any networking cables. In the end, what happened was that an Electrician been 'promoted' to 'network technican' for a day. The guy's boss must have said It's just a tiny cable...a little smaller than your used to, just do as usual. Poor guy he stripped 112 wires (7 cables * 2 ends * 8 wires) by hand," Jon B. wrote.

 

Adam G. writes, "Something tells me I will not be contacted shortly."

 

Robert wrote, "Usually I'm very careful who I'm sharing passwords with, but in this case, I think I can make an exception."

 

[Advertisement] Continuously monitor your servers for configuration changes, and report when there's configuration drift. Get started with Otter today!


CodeSOD: A Policy Exception;

“So, we have a problem. A… a big one.”

Andrea C worked for an insurance company. In terms of software development, a “problem” meant she’d misfiled one of her TPS coversheets or something like that. A big problem meant she’d accidentally checked in code that contained a comment with some profanity in it.

“It’s actually, well, it’s actually huge,” her boss continued.

She’d never had a “huge” problem before.

Someone had discovered that, if they knew a customer’s policy number and were currently logged in- they could see that customer’s policy. It didn’t have to be their policy. Any policy, so long as they had a valid login and the policy number.

That was pretty huge. Andrea hadn’t gotten too deep into that section of the application before, but the original developer had moved on, so it was time for her to dig in. She pulled up the JSP code which served the page.

<%
	boolean globalError = false;

	// SNIP: gets various parameters from HttpPortletRequest. E.g. policyNumber, fiscalCode, etc.
  //fiscalCode is the customer identifier

	try {
		// Security check to see if the policy belongs to the user
		if(!PolicyUtil.isValidPolicyNumber(fiscalCode, policyNumber, endpoint))
			throw new PolicyNumberException();
	} catch(Exception e) {
		globalError = true;
	}

	Policy policy = PolicyUtil.getPolicy(fiscalCode, policyNumber, dateOnlyFormat, endpoint);
%>

<%-- <c:if test="<%=!globalError %>"> --%>
	<div class="header container main_dsh">
		<h2><%=policy.getProductName() %></h2>
		<h4 style="color: white;">N. <%=policyNumber %></h4>
	</div>
<%-- </c:if> --%>	
	// SNIP: displays policy details

Let’s start with names. isValidPolicyNumber isn’t just a validator- it uses the fiscalCode to determine if this customer is allowed to see that policy number. It also checks that the policy number exists, that it’s a valid and active policy. And all that information is encoded into a single boolean value.

And of course, of course if that boolean value is false, we throw an exception. Flow of control by exceptions is always a great choice, especially when the only thing you do with the exception is set a flag.

The sane thing, at this point, is to bail. We can’t display this policy. But the code doesn’t bail, it actually goes right back to the database to fetch the policy. A policy that it may have already decided is not valid in this context.

When Andrea found this much, she thought she’d figured out the actual bug, but reading on revealed the next step. The <c:if> test only surrounds the display of the header.

No one had touched this code in years. Pretty much since their new website launched in 2009, this code had been sitting in production.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!