Go ReadString(..) bug on release but works on debug

Eviatar Gerzi
4 min readMar 25, 2020

--

Photo by: Bart Christiaanse @bartchristiaanse (unsplash.com)

Did you ever encounter an annoying bug that was exist only on release but when you debugged it, it worked fine? It happened to me today and I thought you can learn something new from this small issue.

While working on a CLI tool in Go, I needed to prompt an interactive question to the user, receive the input and check if it is “y” (yes) or “n” (no), sounds simple right? Because it supposed to be!

But every time I typed “y” and pressed Enter, the condition that checked if I received “y”, was false. Here is the code, see if you can find it by yourself (you already have a clue) before I will write what is the problem:

package main

import (
"bufio"
"fmt"
"os"
"strings"
)

func main(){

reader := bufio.NewReader(os.Stdin)
fmt.Print("[*] Do you approve ? (y\\n): ")

text, err := reader.ReadString('\n')
if err != nil {
fmt.Println("[*] Error while reading input")
os.Exit(1)
}
text = strings.TrimSuffix(text, "\n")

if strings.ToLower(text) == "y" {
fmt.Println("[*] Approved")
} else {
fmt.Println("[*] Not approved")
}
}

In debug it works!

I thought I can find the problem simply by debugging it. I started the debugger, going step by step. The value of text is “y” and I don’t see any problem with this. No white spaces, only “y”. Stepping to the condition, and... it enters the condition with the “y”!

Wait wait wait, I want to understand. If I run the program without debug mode, it doesn’t match the “y” I typed. But is it IDE issue or debug vs release issue? It seems that this is related to the IDE I am using which is GoLand.

When I run the program with GoLand in release mode, it also works fine. So running with the GoLand IDE is OK, but still, when I run the program from the command line it behaves differently, it doesn’t enter the condition of “y”.

Debugging with old-style

This is the time of using the old-style debugging, printing on release mode like good old days :)

My guess is that that text variable contains some other characters. This is the only thing that can cause it and make sense. The simple thing I did is to print the text with this row:

fmt.Println("text: ", text, "err: ", err)

After I printed it, something weird happened:

“text” is missing (without IDE)

It didn’t print “text: “ nor the variable text. When I ran it with GoLand, it does:

OK, maybe there are some hidden characters. Time to pull out the big guns, I added the killer rows:

myBytes := []byte(text)
fmt.Println(myBytes)

Let’s see what is going on:

Well well well, as I remember from my ASCII training, 121 is “y” and “13” is … (drums effect): CR carriage return.

When running the same compiled program with the IDE, it seems that GoLand removed the carriage return automatically:

Run with GoLand

The fix is simple, changing the following row by adding “\r”:

text = strings.TrimSuffix(text, "\r\n")

Summary

I think most of the people usually don’t think about the IDE as one that can cause issues. It runs the program like we will run it without it. But this small bug shows the there are differences and this is why you need to take it into consideration. Next time, it might be more tricky bug and you want to make sure that the IDE is not “sabotaging“ your code.

--

--

Eviatar Gerzi
Eviatar Gerzi

Written by Eviatar Gerzi

Security researcher interested in reversing, solving CTFs, malware analysis, penetration testing and DevOps security (docker and Kubernetes)

No responses yet