Bug 63067 - Misuse of setjmp in DCTStream
Summary: Misuse of setjmp in DCTStream
Status: RESOLVED FIXED
Alias: None
Product: poppler
Classification: Unclassified
Component: general (show other bugs)
Version: unspecified
Hardware: Other All
: medium normal
Assignee: poppler-bugs
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-04-03 09:41 UTC by rodrigo
Modified: 2013-04-04 21:10 UTC (History)
0 users

See Also:
i915 platform:
i915 features:


Attachments
Fix use of setjmp (2.43 KB, patch)
2013-04-04 18:28 UTC, rodrigo
Details | Splinter Review

Description rodrigo 2013-04-03 09:41:53 UTC
According to POSIX and the C spec, `setjmp` should only be used as the "entire controlling expression" of a selection of iteration statement, possibly with `!` or `== 0` or similar.

But in DCTStream.cc is the line:

    if (!setjmp(err.setjmp_buffer) && jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED)

If I'm not mistaken, that is undefined behavior. Not that it broke anything yet...

Instead it should be:

    if (!setjmp(err.setjmp_buffer))
    {
      if (jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED)
Comment 1 Albert Astals Cid 2013-04-03 23:37:50 UTC
The man page I have here does not say that, do you have a link to the POSIX or C spec at hand?
Comment 2 rodrigo 2013-04-04 00:29:49 UTC
(In reply to comment #1)
> The man page I have here does not say that, do you have a link to the POSIX
> or C spec at hand?

You can read what POSIX says at [0].

The C99 specification is at [1] (section 7.3.1.1) but the wording is almost identical, as this function is 'ISO C aligned'.

Also, many linux distributions have a package named manpages-posix or the like. If you have it installed you can request the POSIX version of the man page by running:

$ man 3p setjmp

But now that I read the 'longjmp(3p)' [2] with more detail, I think that there are bigger problems ahead... Note the following quote:

> if [when calling longjmp] the function containing the invocation of setjmp() has terminated execution [...] the behavior is undefined.

And I think that this happens a lot, as `setjmp` is called from a lot of helper functions. I think that the right thing to do should be to call it only once at the beginning of the main decompression function. The problem is that such a call cannot be encapsulated into the DCTStream class, it had to be done from the user code, and that is not very nice.

[0]: http://pubs.opengroup.org/onlinepubs/009696799/functions/setjmp.html
[1]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf 
[2]: http://pubs.opengroup.org/onlinepubs/009696799/functions/longjmp.html
Comment 3 rodrigo 2013-04-04 07:28:32 UTC
> But now that I read the 'longjmp(3p)' [2] with more detail, I think that
> there are bigger problems ahead... Note the following quote:

Replying to myself, that 'longjmp' problem does not exist at all! I've reread the code of DCTStream.cc and every time a jpeg_* function is called, there is a call to 'setjmp' in that very same function, so the call to 'longjmp' will always be valid.

The original corrections about 'setjmp' still stands, of course.
Comment 4 Thomas Freitag 2013-04-04 08:56:46 UTC
(In reply to comment #0)
> According to POSIX and the C spec, `setjmp` should only be used as the
> "entire controlling expression" of a selection of iteration statement,
> possibly with `!` or `== 0` or similar.
> 
> But in DCTStream.cc is the line:
> 
>     if (!setjmp(err.setjmp_buffer) && jpeg_read_header(&cinfo, TRUE) !=
> JPEG_SUSPENDED)

This is NOT a iteration statement, iteration statements are "for", "while" and similar!
 
> 
> If I'm not mistaken, that is undefined behavior. Not that it broke anything
> yet...
> 
> Instead it should be:
> 
>     if (!setjmp(err.setjmp_buffer))
>     {
>       if (jpeg_read_header(&cinfo, TRUE) != JPEG_SUSPENDED)

Okay, okay, the order of execution of an if condition is undefined in C/C++ (other than if Java, where You can count on that it will be done from left to right). BUT I never see an c/c++ compiler which does it in another way.
But if we are puristic: Yes, it should be in that way!
Comment 5 rodrigo 2013-04-04 09:11:29 UTC
(In reply to comment #4)
> (In reply to comment #0)
> >     if (!setjmp(err.setjmp_buffer) && jpeg_read_header(&cinfo, TRUE) !=
> > JPEG_SUSPENDED)
> 
> This is NOT a iteration statement, iteration statements are "for", "while"
> and similar!
No, it is a *selection statement*. The other one is 'switch'. If it weren't it would be even a worst case, because the 'setjmp' specification enumerates the places where it is allowed to be used, not where it is forbidden.

> Okay, okay, the order of execution of an if condition is undefined in C/C++.

No, that's not the problem. The order of evaluation of an expression is unspecified, except where otherwise indicated. But the && operator does insert a sequence point, so the left part is always evaluated before the right one. The problem is not the order of evaluation but that 'setjmp' is not allowed to be used here.

> But if we are puristic: Yes, it should be in that way!

I agree that I'm being puristic, but the change costs nothing and may avoid nasty surprises, particularly in weird architectures. And nasty looks from the pedantics...
Comment 6 Albert Astals Cid 2013-04-04 17:20:20 UTC
Can you please provide a patch?
Comment 7 rodrigo 2013-04-04 18:28:50 UTC
Created attachment 77439 [details] [review]
Fix use of setjmp

Sure! I'd assumed that such a simple change could be done on the fly. But if you prefer a patch...
Comment 8 Albert Astals Cid 2013-04-04 21:10:49 UTC
Fixed in master


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.