Rebuilding Debian 6.0 (Squeeze) Bash package

Background

I have been running Debian stable on some servers, since the time when the stable version was 3.0 (Woody). Running stable means that I do not get the lates and greates versions of different software packages, but it also means I know what I have, and the risk of things breaking due to a new version of a package are minimal. In my opinion this is perfect for servers.

When a security issue is discovered, the Debian Security Team will fix the problem, and usually backport it to the version of a particular software package, that is in Debian stable. So with Debian Stable you do not get new software, but you do get security updates.

Problem

When Debian Squeeze (6.0) went stable, I installed it on a couple of servers, and was very happy with the new systems. A lot of the software that I previously had to manually compile was now included in Debian, and in a version that was new enough to work with what I needed it to.

Then, after a while, I installed squeeze on a 64-bit machine (x86_64). This also worked fine, but I discovered an annoying bug in the version of Bash that ships with Debian Squeeze. It looks like this:

[root@server ~]# export PS1='[\u@\h \W]\$ '
[root@server ~]# cd /home/ ; pwd
/home
[root@server hmee]# cd /boot/ ; pwd
/boot
[root@server bott]# cd /proc/ ; pwd
/proc
[root@server pocc]# cd /lib64/ ; pwd
/lib64
[root@server li664]# cd /media/ ; pwd
/media

Notice how the prompt does not correctly expand \W for these directories. It works fine with \w, but having the full path in the prompt is not always desireable (although I did use \w as a work-around until I finally patched Bash, as described here).

The problem occurs for all directories directly under /, which have either 4 or 5 characters in their name (so also for /root, except when you are logged in as root user, since it will then simply show ˜). After googling around for some time, I did find this problem described in several places, including the Debian bug report. Note that the bug report also contains a link to where the problem occurs: parse.y line 5153 uses strcpy, but the strings are overlapping, so the result is not defined according to the C-specification. Se below for my fix.

Unfortunately, it seems this is not considered important enough to be fixed in Squeeze (I guess it is not a security issue), so after being annoyed with this for a long time, I finally decided to try and fix it. And because others might be just as annoyed as I was, I decided to write this page, to describe how I fixed it, and allowing others to get through a little bit easier.

Preparing for the fix

In order to patch the Bash package, we will first need to install the needed tools to build the package, and also we need the source for the Bash package.

As root, do the following:

[root@server ~]# aptitude build-dep bash

This ensures the build dependencies for Bash are met (it will install libraries, compilers and tools needed to build tha Bash package).

In order to build the package, we also need dch and debuild from the devscripts-package, so make sure it is installed:

[root@server ~]# aptitude install devscripts

We are now ready to extract the source code for the Bash package. Do this as a normal user:

[spiff@server ~]$ mkdir bashbuild
[spiff@server ~]$ cd bashbuild
[spiff@server bashbuild]$ apt-get source bash
Reading package lists... Done
Building dependency tree
Reading state information... Done
Need to get 2.723 kB of source archives.
Get:1 http://ftp.dk.debian.org/debian/ squeeze/main bash 4.1-3 (dsc) [1.189 B]
Get:2 http://ftp.dk.debian.org/debian/ squeeze/main bash 4.1-3 (tar) [2.640 kB]
Get:3 http://ftp.dk.debian.org/debian/ squeeze/main bash 4.1-3 (diff) [81,7 kB]
Fetched 2.723 kB in 2s (1.076 kB/s)
dpkg-source: info: extracting bash in bash-4.1
dpkg-source: info: unpacking bash_4.1.orig.tar.gz
dpkg-source: info: applying bash_4.1-3.diff.gz

The source has now been downloaded and extracted, but for the Bash package, the original Bash source is still in an archive called bash-4.1dfsg.tar.xz. Normally, when the Bash package is built, this will extract the original Bash source, apply the patches located in bash-4.1/debian/patches/, and then build the package. I found the best way to achieve this was to also make a debian patch, but first we need to extract the source code, in order to create the patch:

[spiff@server bashbuild]$ cd bash-4.1/
[spiff@server bash-4.1]$ tar -xvJf bash-4.1dfsg.tar.xz
[spiff@server bash-4.1]$ mv bash-4.1 bash

For the sake of clarity, the extracted source has been moved tothe directory bash-4.1/bash/. This directory is only temporary, so we can make the patch that will be applied during build.

Patching the source

To make the patch, make a copy of bash-4.1/bash/parse.y, for reference:

[spiff@server bash-4.1]$ cp -a bash/parse.y bash/parse.y.orig

In order to fix the problem, open the file bash-4.1/bash/parse.y in your favorite editor, and go to line 5155:

5152:                 {
5153:                   t = strrchr (t_string, '/');
5154:                   if (t)
5155:                     strcpy (t_string, t + 1);
5156:                 }

As previously mentioned, the problem is the use of strcpy on overlapping regions. My fix is to replace line 5155 with:

memmove (t_string, t + 1, strlen( t + 1 ) + 1);

This emulates the strcpy behavior by using strlen to find the 0-termination, but fixes the problem by using memmove instead of strcpy. memmove handles overlapping memory regions correctly.

Save the modified file, and we can now create the patch:

[spiff@server bash-4.1]$ diff -u bash/parse.y.orig bash/parse.y > backslash-w.patch

We now need to include this patch among the other debian specific patches. The debian patch is actually a small script, that includes the patch, and applies it when run. This is created from the patch we just made, by prepending the file debian/patches/template.dpatch. We create the debian patch file as debian/patches/backslash-w.dpatch like this:

[spiff@server bash-4.1]$ cd debian/patches/
[spiff@server patches]$ cat template.dpatch ../../backslash-w.patch > backslash-w.dpatch

After this, edit the file ( bash-4.1/debian/patches/backslash-w.dpatch), and insert a description of what has been changed in the line starting with # DP:. This is what I wrote on that line:

# DP: Fix expansion of backslash-W in prompt on x86_64

In case you are reading through this guide, but want to save yourself the hassle of creating the dpatch-file, you can get backslash-w.dpatch here and put it in bash-4.1/debian/patches/.

Now that we have the dpatch-file, we should probably clean up the stuff that is no longer needed:

[spiff@server patches]$ cd ../..
[spiff@server bash-4.1]$ rm -fr bash backslash-w.patch

We also need to add our dpatch-file to the list of patches to be applied during build. Open the file bash-4.1/debian/rules in your favorite editor, and add the following line to the end of the debian_patches-declatation (on line 526 in this case):

backslash-w \

Building the package

We are now all set to build the package. We will build this as a local copy, which means it will have a revision that is slightly newer than the current Bash package in Debian Squeeze. This way, if there is a security fix at some point, we will get back to running the official Squeeze version, and we may have to build a new package again, but at least we will not run the risk of having a security hole.

In order to build the package as a local revision, we use dch, which will find the changes we made to the source and ask us to write the changelog entry. Issue this command:

[spiff@server bash-4.1]$ dch --local mho

The arguments tell dch that this is a local package, identified by the suffix mho, so the entire package name will be bash-4.1-3mho1.

The above command will open up your configured editor, to let you enter a changelog entry for the modifications you made. This is what I wrote:

* Fix expansion of backslash-W in PS1-prompt on x86_64.

And finally we can build the new package:

[spiff@server bash-4.1]$ debuild -us -uc

The option -us tells dpkg-buildpackage not to sign the source package.

The option -uc tells dpkg-buildpackage not to sign the .changes-file.

Hopefully the build completes to your satisfaction:

[spiff@server bashbuild]$ cd bash-4.1/
[spiff@server bashbuild]$ ls -F
bash-4.1/                     bash_4.1-3mho1.diff.gz
bash_4.1-3.diff.gz            bash_4.1-3mho1.dsc
bash_4.1-3.dsc                bash_4.1.orig.tar.gz
bash_4.1-3mho1_amd64.build    bash-builtins_4.1-3mho1_amd64.deb
bash_4.1-3mho1_amd64.changes  bash-doc_4.1-3mho1_all.deb
bash_4.1-3mho1_amd64.deb      bash-static_4.1-3mho1_amd64.deb

We can now install the new package (need to do this as root, of course):

[root@server ~]# cd /home/spiff/bashbuild/
[root@server bashbuild]# dpkg --install bash_4.1-3mho1_amd64.deb

If you trust me, and want to save the hassle of building the package yourself, you can download bash_4.1-3mho1_amd64.deb (1.3MB).

Last updated: 2012.04.30