To get a trial key
fill out the form below
Team License (standard version)
Enterprise License (extended version)
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Request our prices
New License
License Renewal
--Select currency--
USD
EUR
GBP
RUB
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
I am interested to try it on the platforms:
* By clicking this button you agree to our Privacy Policy statement

** This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Message submitted.

Your message has been sent. We will email you at


If you haven't received our response, please do the following:
check your Spam/Junk folder and click the "Not Spam" button for our message.
This way, you won't miss messages from our team in the future.

>
>
>
Celebrating the 30-th anniversary of th…

Celebrating the 30-th anniversary of the first C++ compiler: let's find the bugs in it

Nov 05 2015

Cfront is a C++ compiler which came into existence in 1983, and was developed by Bjarne Stroustrup. At that time it was known as "C with Classes". Cfront had a complete parser, symbol tables, and built a tree for each class, function, etc. Cfront was based on CPre. Cfront defined the language until circa 1990. Many of the obscure corner cases in C++, are related to the Cfront implementation limitations. The reason for this, is that Cfront performed translation from C++ to C. In short, Cfront is a sacred artifact for a C++ programmer. So I just couldn't help checking such a project.

0355_CFront/image1.png

Introduction

The idea to check Cfront occurred to me after reading an article devoted to the 30-th anniversary of the first Release version of this compiler: "30 YEARS OF C++". I contacted Bjarne Stroustrup to get the source code of Cfront. For some reason I thought it would be a great hassle getting the code; but it turned out to be quite easy. This source code is open, available for everybody and can be found here: http://www.softwarepreservation.org/projects/c_plus_plus/

I've decided to check the first commercial version of Cfront, released in October, 1983 as it's this version that turned 30 this year.

Bjarne warned me that checking Cfront could be troublesome:

Please remember this is *very* old software designed to run on a 1MB 1MHz machine, and also used on original PCs (640KB). It was also done by one person (me) as only part of my full time job.

Indeed, to check such a project was impossible. At that time, for instance, to separate a class name from a function name they used a simple dot (.) instead of double colon (::). For example:

inline Pptr type.addrof() { return new ptr(PTR,this,0); }

Our PVS-Studio analyzer wasn't ready for this. So I had to ask our colleague to look through the code, and correct such spots manually. It really helped, although there still were some troubles. When the analyzer was checking some fragments, at times it got quite confused, and was refusing to do the analysis. Nevertheless, I did manage to check the project.

I should say right away, I haven't found anything crucial. I think there are 3 reasons why PVS-Studio hasn't found serious bugs:

  • The project size is small. It's just 100 KLOC in 143 files.
  • The code is of high quality.
  • PVS-Studio analyzer didn't understand some fragments of the code.

"Talk is cheap. Show me the code" (c) Linus Torvalds

So, enough talking. I guess the readers are here to see at least one error of THE Stroustrup. Let's have a look at the code.

Fragment 1.

typedef class classdef * Pclass;

#define PERM(p) p->permanent=1

Pexpr expr.typ(Ptable tbl)
{
  ....
  Pclass cl;
  ....
  cl = (Pclass) nn->tp;
  PERM(cl);
  if (cl == 0) error('i',"%k %s'sT missing",CLASS,s);
  ....
}

PVS-Studio warning: V595 The 'cl' pointer was utilized before it was verified against nullptr. Check lines: 927, 928. expr.c 927

The 'cl' pointer can be equal to NULL. The if (cl == 0) check indicates that. What's worse is that this pointer gets dereferenced before this check. It occurs in the PERM macro.

So if we open the macro, we get:

cl = (Pclass) nn->tp;
cl->permanent=1
if (cl == 0) error('i',"%k %s'sT missing",CLASS,s);

Fragment 2.

The same here. The pointer was dereferenced, and only then was it checked:

Pname name.normalize(Pbase b, Pblock bl, bit cast)
{
  ....
  Pname n;
  Pname nn;
  TOK stc = b->b_sto;
  bit tpdf = b->b_typedef;
  bit inli = b->b_inline;
  bit virt = b->b_virtual;
  Pfct f;
  Pname nx;
  if (b == 0) error('i',"%d->N.normalize(0)",this);
  ....
}

PVS-Studio warning: V595 The 'b' pointer was utilized before it was verified against nullptr. Check lines: 608, 615. norm.c 608

Fragment 3.

int error(int t, loc* lc, char* s ...)
{
  ....
  if (in_error++)
    if (t!='t' || 4<in_error) {
      fprintf(stderr,"\nUPS!, error while handling error\n");
      ext(13);
    }
  else if (t == 't')
    t = 'i';
  ....
}

PVS-Studio warning: V563 It is possible that this 'else' branch must apply to the previous 'if' statement. error.c 164

I am not sure if there is an error here or not, but the code is formatted incorrectly. 'Else' refers to the closest 'if'. This is why the code doesn't execute in the way it should. If we format it, we'll have:

if (in_error++)
  if (t!='t' || 4<in_error) {
    fprintf(stderr,"\nUPS!, error while handling error\n");
    ext(13);
  } else if (t == 't')
    t = 'i';

Fragment 4.

extern
genericerror(int n, char* s)
{
  fprintf(stderr,"%s\n",
          s?s:"error in generic library function",n);
  abort(111);
  return 0;
};

PVS-Studio warning: V576 Incorrect format. A different number of actual arguments is expected while calling 'fprintf' function. Expected: 3. Present: 4. generic.c 8

Note the format specifiers: "%s". The string will be printed, but the 'n' variable won't be used.

Miscellaneous:

Unfortunately (or maybe not) I won't be able to show you anything else that could look like real errors. The analyzer issued some warnings which could be worth looking at, but they are not really serious. For example, the analyzer didn't like some global variable names:

extern int Nspy, Nn, Nbt, Nt, Ne, Ns, Nstr, Nc, Nl;

PVS-Studio warning: V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename 'Nn' variable. cfront.h 50

Another example: to print pointer values by means of fprintf() function Cfront uses the "%i" specificator. In the modern version of the language we have "%p". But as far as I understand, there was no "%p" 30 years ago, and the code was totally correct.

Thought-provoking observations

This pointer

My attention was drawn by the fact that previously 'this' pointer was used in a different way. A couple of examples:

expr.expr(TOK ba, Pexpr a, Pexpr b)
{
  register Pexpr p;

  if (this) goto ret;
  ....
  this = p;
  ....
}

inline toknode.~toknode()
{
  next = free_toks;
  free_toks = this;
  this = 0;
}

As you see, it wasn't forbidden to change 'this' value. Now it's not only prohibited to change the pointer, but also to compare 'this' to null, as this comparison has completely lost any sense. (Still Comparing "this" Pointer to Null?)

This is the place for paranoia

I've also come across an interesting fragment. Nothing seems safe anymore. I liked this code fragment:

/* this is the place for paranoia */
if (this == 0) error('i',"0->Cdef.dcl(%d)",tbl);
if (base != CLASS) error('i',"Cdef.dcl(%d)",base);
if (cname == 0) error('i',"unNdC");
if (cname->tp != this) error('i',"badCdef");
if (tbl == 0) error('i',"Cdef.dcl(%n,0)",cname);
if (tbl->base != TABLE) error('i',"Cdef.dcl(%n,tbl=%d)",
                              cname,tbl->base);

Bjarne Stroustrup's commentaries

  • Cfront was bootstrapped from Cpre, but it was a complete rewrite. There wasn't a line of Cpre code in Cfront
  • The use-before-test-of-0 bad is of course bad, but curiously, the machine and OS i mostly used (DEC and research Unix) had page zero write protected, so that bug could not have been triggered without being caught.
  • The if-then-else bug (or not) is odd. I read the source, it's not just misformatted, it's incorrect; but curiously, that doesn't matter: the only difference is a slight difference in the error message used before terminating. No wonder I did not spot it.
  • Yes, I should have used more readable names. I hadn't counted on having other people maintain this program for years (and I'm a poor typist).
  • Yes, there were no %p then
  • Yes, the rules for "this" changed
  • The paranoia test is in the compiler's main loop. My thought was that if anything when wrong with the software or hardware, one of those tests were likely to fail. At least once, it caught the effect of a bug in the code generator used to build Cfront. I think all significant programs should have a "paranoia test" against "impossible" errors.

Conclusion:

It's really hard to estimate the significance of Cfront. It influenced the development of a whole sphere of programming, and gave this world an everlasting C++ language which continues developing. I am really grateful to Bjarne for all the work he has done in creating and developing C++. Thank you. In my turn, I was really glad to dig into the code of this wonderful compiler.

I thank all our readers for their attention, and wish you to have less bugs.

Popular related articles
Appreciate Static Code Analysis!

Date: Oct 16 2017

Author: Andrey Karpov

I am really astonished by the capabilities of static code analysis even though I am one of the developers of PVS-Studio analyzer myself. The tool surprised me the other day as it turned out to be sma…
Free PVS-Studio for those who develops open source projects

Date: Dec 22 2018

Author: Andrey Karpov

On the New 2019 year's eve, a PVS-Studio team decided to make a nice gift for all contributors of open-source projects hosted on GitHub, GitLab or Bitbucket. They are given free usage of PVS-Studio s…
Technologies used in the PVS-Studio code analyzer for finding bugs and potential vulnerabilities

Date: Nov 21 2018

Author: Andrey Karpov

A brief description of technologies used in the PVS-Studio tool, which let us effectively detect a large number of error patterns and potential vulnerabilities. The article describes the implementati…
PVS-Studio ROI

Date: Jan 30 2019

Author: Andrey Karpov

Occasionally, we're asked a question, what monetary value the company will receive from using PVS-Studio. We decided to draw up a response in the form of an article and provide tables, which will sho…
How PVS-Studio Proved to Be More Attentive Than Three and a Half Programmers

Date: Oct 22 2018

Author: Andrey Karpov

Just like other static analyzers, PVS-Studio often produces false positives. What you are about to read is a short story where I'll tell you how PVS-Studio proved, just one more time, to be more atte…
The Last Line Effect

Date: May 31 2014

Author: Andrey Karpov

I have studied many errors caused by the use of the Copy-Paste method, and can assure you that programmers most often tend to make mistakes in the last fragment of a homogeneous code block. I have ne…
The Ultimate Question of Programming, Refactoring, and Everything

Date: Apr 14 2016

Author: Andrey Karpov

Yes, you've guessed correctly - the answer is "42". In this article you will find 42 recommendations about coding in C++ that can help a programmer avoid a lot of errors, save time and effort. The au…
The Evil within the Comparison Functions

Date: May 19 2017

Author: Andrey Karpov

Perhaps, readers remember my article titled "Last line effect". It describes a pattern I've once noticed: in most cases programmers make an error in the last line of similar text blocks. Now I want t…
The way static analyzers fight against false positives, and why they do it

Date: Mar 20 2017

Author: Andrey Karpov

In my previous article I wrote that I don't like the approach of evaluating the efficiency of static analyzers with the help of synthetic tests. In that article, I give the example of a code fragment…
PVS-Studio for Java

Date: Jan 17 2019

Author: Andrey Karpov

In the seventh version of the PVS-Studio static analyzer, we added support of the Java language. It's time for a brief story of how we've started making support of the Java language, how far we've co…

Comments (0)

Next comments

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
This website uses cookies and other technology to provide you a more personalized experience. By continuing the view of our web-pages you accept the terms of using these files. If you don't want your personal data to be processed, please, leave this site.
Learn More →
Accept