After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 769229 - Generates C code with format errors
Generates C code with format errors
Status: RESOLVED FIXED
Product: vala
Classification: Core
Component: general
0.34.x
Other Linux
: Normal normal
: ---
Assigned To: Vala maintainers
Vala maintainers
: 725995 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2016-07-27 15:13 UTC by Bastien Nocera
Modified: 2016-11-12 08:12 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Bastien Nocera 2016-07-27 15:13:17 UTC
vala-0.32.1-1.fc24.x86_64

This code in vala:
throw new PluginError.NOT_A_PLUGIN (_("Couldn't create a new instance of plugin in '%s'."), module_path);
is transformed into this C:
_tmp5_ = _ ("Couldn't create a new instance of plugin in '%s'.");
_tmp6_ = self->priv->module_path;
_tmp7_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _tmp5_, _tmp6_);

Which throws errors:
gnome-games/src/core/plugin-registrar.c: In function ‘games_plugin_registrar_new_plugin’:
gnome-games/src/core/plugin-registrar.c:366:3: error: format not a string literal, argument types not checked [-Werror=format-nonliteral]
   _tmp7_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _tmp5_, _tmp6_);
   ^~~~~~

Using variables when literals are supposed to be used is a potential security problem, which is why GCC throws those errors. Vala shouldn't be generating C code that cannot compile.
Comment 1 Al Thomas 2016-07-27 15:25:34 UTC
Do you get the same error when not using the gettext macro _() ?
Comment 2 Bastien Nocera 2016-07-27 15:48:55 UTC
(In reply to Al Thomas from comment #1)
> Do you get the same error when not using the gettext macro _() ?

No, because vala transforms that into:
_tmp6_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, "Couldn't create a new instance of plugin in '%s'.", _tmp5_);

Note that this C code also works:
_tmp6_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _("Couldn't create a new instance of plugin in '%s'."), _tmp5_);
Comment 3 Al Thomas 2016-07-28 11:47:41 UTC
Thanks for supplying more details.

Some further analysis:

The Problem
-----------
A useful explanation of using -Wformat -Werror=format-security:
https://fedoraproject.org/wiki/Format-Security-FAQ

Fedora and Ubuntu now build using -Wformat -Werror=format-security:
https://fedorahosted.org/fesco/ticket/1185
https://wiki.ubuntu.com/ToolChain/CompilerFlags#A-Wformat_-Wformat-security

The Vala User Code Solution
---------------------------
Vala appears to do the right thing, so function calls that expect printf style parameters are generated as ( "%s", my_variable ). Vala also provides the [PrintfFormat] attribute to mark functions that expect printf style parameters. Compile the following with valac --ccode example.vala:

const string GETTEXT_PACKAGE = "example";

void main() {
	Intl.setlocale ();
	string a = "abcdef";
	print (a);
	string b = dgettext ( "example", "abcdef");
	message (b);
	string c = _ ("abcdef");
	print (c);

	string d = test ( "abcdef" );
	print( d );
	test( test( "abcdef" ));
}

[PrintfFormat]
string test (string a, ...) {
	return a;
}

The nested call to test() produces:
        _tmp6_ = test ("abcdef");
        _tmp7_ = _tmp6_;
        _tmp8_ = test ("%s", _tmp7_);
when the [PrintfFormat] attribute is used.

The Scope of this Bug
---------------------
This narrows the scope of this bug report to throwing a new GError in Vala when the message is generated from a function, such as used by gettext.

The C code to call g_error_new is generated in codegen/valaccodebasemodule.vala, but uses an existing argument list created earlier in the compilation process. There may be some way to add the PrintfFormat attribute somewhere like vala/valathrowstatement.vala or vala/valaerrottype.vala. This needs further investigation.
Comment 4 Al Thomas 2016-08-01 16:30:55 UTC
Scrap the conclusion of the last comment. It looks like this could be because the translation function is a non literal, as per the error message! 

The following:

void main() {
    print( translate_to( "module is %s\n" ), "test" );
}

string translate_to ( string a ) {
    return a;
}

compiled on Fedora 23 with:

valac test.vala -X -Wformat -X -Werror=format-nonliteral

produces the format not a string literal error. Using:

valac ju.vala -X -Wformat -X -Werror=format-security

does not produce the error.

To overcome your problem you could:
a) switch to -Werror=format-security, this seems to be what the major distros are using
b) re-write your throw statement as:
throw new PluginError.NOT_A_PLUGIN ( "%s '%s'", _("Couldn't create a new instance of plugin in"), module_path);

I'm not sure why _tmp6_ = g_error_new (GAMES_PLUGIN_ERROR, GAMES_PLUGIN_ERROR_NOT_A_PLUGIN, _("Couldn't create a new instance of plugin in '%s'."), _tmp5_); compiles with -Werror=format-nonliteral
The way I'm reading it now, is that gettext shouldn't be used with printf tokens if you want to compile with -Werror=format-nonliteral. _() is a runtime translation after all.
Comment 5 Christophe Fergeau 2016-08-03 10:15:57 UTC
(In reply to Al Thomas from comment #4)

> b) re-write your throw statement as:
> throw new PluginError.NOT_A_PLUGIN ( "%s '%s'", _("Couldn't create a new
> instance of plugin in"), module_path);
> 

From a translation of view, rewriting things this way is not correct, the translator may need to move the module_path away from the end of the message eg maybe it will need to be formulated as "In %s it was not possible to create a new instance of the plugin" in some languages.
Comment 6 Al Thomas 2016-08-03 11:43:45 UTC
(In reply to Christophe Fergeau from comment #5)
> From a translation of view, rewriting things this way is not correct, the
> translator may need to move the module_path away from the end of the message
> eg maybe it will need to be formulated as "In %s it was not possible to
> create a new instance of the plugin" in some languages.

Yes, you lose flexibility in the translation. There are tools like msgfmt -c to check ordering of printf tokens in gettext strings, see https://www.gnu.org/software/gettext/manual/html_node/c_002dformat-Flag.html , so re-ordering is a common thing. From what I understand, though, if you really want to use -Werror=format-nonliteral you will lose that flexibility in your translations.

What I'm not finding, when searching on the Internet, is anything about gettext and -Werror=format-nonliteral. From GCC docs ( https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html ) the format-nonliteral option is "warn if the format string is not a string literal and so cannot be checked, unless the format function takes its format arguments as a va_list", whereas format-security is "This is currently a subset of what -Wformat-nonliteral warns about".

The gettext functions do not take a va_list ( see https://www.gnu.org/software/libc/manual/html_node/Translation-with-gettext.html ). I'm not sure that it would make a difference if they did, but the functions take a const char* argument. If that argument is a function that returns a char* or a variable that contains a char* then my understanding is -Wformat-nonliteral will error because the function or variable are nonliterals. Now gettext _() is a macro for a function call, so if you use _() you will get the error when checking for nonliteral. The only way to use _() in those circumstances is to use function_call( "%s", _( "string to translate" )) This loses the flexibility of printf style tokens in the translation string. So you either have to work without them or not use -Werror=format-nonliteral. That's why I suggested the two solutions, maybe I have missed something.
Comment 7 Rico Tzschichholz 2016-09-16 21:59:48 UTC
Isn't this working:

throw new PluginError.NOT_A_PLUGIN (_("Couldn't create a new instance of plugin in '%s'.").printf (module_path));
Comment 8 Rico Tzschichholz 2016-11-03 21:03:05 UTC
commit b905023ff33eb220f86a5a754a8828d89931517a
Author: Jürg Billeter <j@bitron.ch>
Date:   Tue Nov 1 18:03:17 2016 +0100

    codegen: Avoid temporary variables for calls to [FormatArg] methods
    
    This prevents bogus C compiler warnings with -Wformat-nonliteral.
Comment 9 Bastien Nocera 2016-11-07 14:44:19 UTC
That works in my tests, thanks.
Comment 10 Rico Tzschichholz 2016-11-12 08:12:58 UTC
*** Bug 725995 has been marked as a duplicate of this bug. ***