may be a buffer overflow problem

Started by Winter Looover 1 year ago15 messages
#1Winter Loo
winterloo@126.com

Hi hackers,

I am using gcc version 11.3.0 to compile postgres source code. Gcc complains about the following line:

```c
strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
```

with error as:

misc.c:529:17: error: ‘strncpy’ output truncated before terminating nul copying 5 bytes from a string of the same length [-Werror=stringop-truncation]

I find the definition of `sqlca->sqlstate` and it has only 5 bytes. When the statement

```c
strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
```

get executed, `sqlca->sqlstate` will have no '\0' byte which makes me anxious when someone prints that as a string. Indeed, I found the code(in src/interfaces/ecpg/ecpglib/misc.c) does that,

```c
fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
sqlca->sqlcode, sqlca->sqlstate);
```

Is there any chance to fix the code?

#2Daniel Gustafsson
daniel@yesql.se
In reply to: Winter Loo (#1)
1 attachment(s)
Re: may be a buffer overflow problem

On 14 Jun 2024, at 09:38, Winter Loo <winterloo@126.com> wrote:

I find the definition of `sqlca->sqlstate` and it has only 5 bytes. When the statement

```c
strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
```

get executed, `sqlca->sqlstate` will have no '\0' byte which makes me anxious when someone prints that as a string.

sqlstate is defined as not being unterminated fixed-length, leaving the callers
to handle termination.

Indeed, I found the code(in src/interfaces/ecpg/ecpglib/misc.c) does that,

fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
sqlca->sqlcode, sqlca->sqlstate);

This is indeed buggy and need to take the length into account, as per the
attached. This only happens when in the undocumented regression test debug
mode which may be why it's gone unnoticed.

--
Daniel Gustafsson

Attachments:

ecgp_sqlstate.diffapplication/octet-stream; name=ecgp_sqlstate.diff; x-unix-mode=0644Download
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index 2ae989e3e5..110f73d3dd 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -274,8 +274,8 @@ ecpg_log(const char *format,...)
 		/* dump out internal sqlca variables */
 		if (ecpg_internal_regression_mode && sqlca != NULL)
 		{
-			fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
-					sqlca->sqlcode, sqlca->sqlstate);
+			fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %.*s\n",
+					sqlca->sqlcode, (int) sizeof(sqlca->sqlstate), sqlca->sqlstate);
 		}
 
 		fflush(debugstream);
#3Laurenz Albe
laurenz.albe@cybertec.at
In reply to: Winter Loo (#1)
Re: may be a buffer overflow problem

On Fri, 2024-06-14 at 15:38 +0800, Winter Loo wrote:

I am using gcc version 11.3.0 to compile postgres source code. Gcc complains about the following line:

strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));

with error as:

misc.c:529:17: error: ‘strncpy’ output truncated before terminating nul
copying 5 bytes from a string of the same length [-Werror=stringop-truncation]

I find the definition of `sqlca->sqlstate` and it has only 5 bytes. When the statement

strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));

get executed, `sqlca->sqlstate` will have no '\0' byte which makes me anxious
when someone prints that as a string. Indeed, I found the code(in src/interfaces/ecpg/ecpglib/misc.c) does that,

fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
sqlca->sqlcode, sqlca->sqlstate);

Is there any chance to fix the code?

I agree that that is wrong.

We could either use memcpy() to avoid the warning and use a format string
with %.*s in fprintf(), or we could make the "sqlstate" one byte longer.

I think that the second option would be less error-prone.

Yours,
Laurenz Albe

#4Laurenz Albe
laurenz.albe@cybertec.at
In reply to: Daniel Gustafsson (#2)
Re: may be a buffer overflow problem

On Fri, 2024-06-14 at 09:55 +0200, Daniel Gustafsson wrote:

On 14 Jun 2024, at 09:38, Winter Loo <winterloo@126.com> wrote:

I find the definition of `sqlca->sqlstate` and it has only 5 bytes. When the statement

```c
strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
```

get executed, `sqlca->sqlstate` will have no '\0' byte which makes me anxious when someone prints that as a string.

sqlstate is defined as not being unterminated fixed-length, leaving the callers
to handle termination.

Indeed, I found the code(in src/interfaces/ecpg/ecpglib/misc.c) does that,

fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
sqlca->sqlcode, sqlca->sqlstate);

This is indeed buggy and need to take the length into account, as per the
attached. This only happens when in the undocumented regression test debug
mode which may be why it's gone unnoticed.

So you think we should ignore that compiler warning?
What about using memcpy() instead of strncpy()?

Yours,
Laurenz Albe

#5Daniel Gustafsson
daniel@yesql.se
In reply to: Laurenz Albe (#4)
Re: may be a buffer overflow problem

On 14 Jun 2024, at 10:06, Laurenz Albe <laurenz.albe@cybertec.at> wrote:

So you think we should ignore that compiler warning?

We already do using this in meson.build:

# Similarly disable useless truncation warnings from gcc 8+
'format-truncation',
'stringop-truncation',

--
Daniel Gustafsson

#6Laurenz Albe
laurenz.albe@cybertec.at
In reply to: Daniel Gustafsson (#5)
Re: may be a buffer overflow problem

On Fri, 2024-06-14 at 10:10 +0200, Daniel Gustafsson wrote:

On 14 Jun 2024, at 10:06, Laurenz Albe <laurenz.albe@cybertec.at> wrote:

So you think we should ignore that compiler warning?

We already do using this in meson.build:

  # Similarly disable useless truncation warnings from gcc 8+
  'format-truncation',
  'stringop-truncation',

Right; and I see that -Wno-stringop-truncation is also set if you build
with "make". So your patch is good.

I wonder how Winter Loo got to see that warning...

Yours,
Laurenz Albe

#7Daniel Gustafsson
daniel@yesql.se
In reply to: Laurenz Albe (#6)
Re: may be a buffer overflow problem

On 14 Jun 2024, at 10:29, Laurenz Albe <laurenz.albe@cybertec.at> wrote:

On Fri, 2024-06-14 at 10:10 +0200, Daniel Gustafsson wrote:

On 14 Jun 2024, at 10:06, Laurenz Albe <laurenz.albe@cybertec.at> wrote:

So you think we should ignore that compiler warning?

We already do using this in meson.build:

# Similarly disable useless truncation warnings from gcc 8+
'format-truncation',
'stringop-truncation',

Right; and I see that -Wno-stringop-truncation is also set if you build
with "make". So your patch is good.

Thanks for looking! I will apply it backpatched all the way down as this has
been wrong since 2006.

I wonder how Winter Loo got to see that warning...

And it would be interesting to know if that was the only warning, since error.c
in ECPG performs the exact same string copy.

--
Daniel Gustafsson

#8Winter Loo
winterloo@126.com
In reply to: Daniel Gustafsson (#7)
Re:Re: may be a buffer overflow problem

Thanks for looking! I will apply it backpatched all the way down as this has

been wrong since 2006.

I wonder how Winter Loo got to see that warning...

I was compiling source code of postgres version 13 and the building flags is changed in my development environment.

And it would be interesting to know if that was the only warning, since error.c >in ECPG performs the exact same string copy.

Yes, that was the only warning. I searched all `sqlstate` words in ecpg directory, there's no other dubious problems.

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Daniel Gustafsson (#2)
Re: may be a buffer overflow problem

Daniel Gustafsson <daniel@yesql.se> writes:

This is indeed buggy and need to take the length into account, as per the
attached. This only happens when in the undocumented regression test debug
mode which may be why it's gone unnoticed.

Seeing that this code is exercised thousands of times a day in the
regression tests and has had a failure rate of exactly zero (and
yes, the tests do check the output), there must be some reason
why it's okay.

regards, tom lane

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#9)
Re: may be a buffer overflow problem

I wrote:

Seeing that this code is exercised thousands of times a day in the
regression tests and has had a failure rate of exactly zero (and
yes, the tests do check the output), there must be some reason
why it's okay.

After looking a little closer, I think the reason why it works in
practice is that there's always a few bytes of zero padding at the
end of struct sqlca_t.

I don't see any value in changing individual code sites that are
depending on that, because there are surely many more, both in
our own code and end users' code. What I suggest we ought to do
is formalize the existence of that zero pad. Perhaps like this:

char sqlstate[5];
+ char sqlstatepad; /* nul terminator for sqlstate */
};

Another way could be to change

- 	char		sqlstate[5];
+ 	char		sqlstate[6];

but I fear that could have unforeseen consequences in code that
is paying attention to sizeof(sqlstate).

Either way there are probably doc adjustments to be made.

regards, tom lane

#11Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#10)
1 attachment(s)
Re: may be a buffer overflow problem

On 14 Jun 2024, at 17:18, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I wrote:

Seeing that this code is exercised thousands of times a day in the
regression tests and has had a failure rate of exactly zero (and
yes, the tests do check the output), there must be some reason
why it's okay.

After looking a little closer, I think the reason why it works in
practice is that there's always a few bytes of zero padding at the
end of struct sqlca_t.

I don't see any value in changing individual code sites that are
depending on that, because there are surely many more, both in
our own code and end users' code. What I suggest we ought to do
is formalize the existence of that zero pad. Perhaps like this:

char sqlstate[5];
+ char sqlstatepad; /* nul terminator for sqlstate */
};

Another way could be to change

- char sqlstate[5];
+ char sqlstate[6];

but I fear that could have unforeseen consequences in code that
is paying attention to sizeof(sqlstate).

Since sqlca is, according to our docs, present in other database systems we
should probably keep it a 5-char array for portability reasons. Adding a
padding character should be fine though.

The attached adds padding and adjust the tests and documentation to match. I
kept the fprintf using %.*s to match other callers. I don't know ECPG well
enough to have strong feelings wrt this being the right fix or not, and the age
of incorrect assumptions arounf that fprintf hints at this not being a huge
problem in reality (should still be fixed of course).

--
Daniel Gustafsson

Attachments:

ecgp_sqlstate-v2.diffapplication/octet-stream; name=ecgp_sqlstate-v2.diff; x-unix-mode=0644Download
diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index e7a53f3c9d..9c247ccc2e 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -5052,6 +5052,7 @@ struct
     long sqlerrd[6];
     char sqlwarn[8];
     char sqlstate[5];
+    char sqlstatepad;
 } sqlca;
 </programlisting>
     (In a multithreaded program, every thread automatically gets its
@@ -5201,6 +5202,11 @@ sqlstate: 42P01
     throughout all applications.  For further information see
     <xref linkend="errcodes-appendix"/>.
    </para>
+   <para>
+    <literal>sqlca.sqlstate</literal> field is a non-terminated character
+    array, in order to guard against accidental uses as a C string it is
+    followed by a padding nul character.
+   </para>
 
    <para>
     <literal>SQLCODE</literal>, the deprecated error code scheme, is a
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
index 2ae989e3e5..a28481eca5 100644
--- a/src/interfaces/ecpg/ecpglib/misc.c
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -52,7 +52,8 @@ static struct sqlca_t sqlca_init =
 	},
 	{
 		'0', '0', '0', '0', '0'
-	}
+	},
+	'\0'
 };
 
 static pthread_key_t sqlca_key;
@@ -274,8 +275,8 @@ ecpg_log(const char *format,...)
 		/* dump out internal sqlca variables */
 		if (ecpg_internal_regression_mode && sqlca != NULL)
 		{
-			fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
-					sqlca->sqlcode, sqlca->sqlstate);
+			fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %.*s\n",
+					sqlca->sqlcode, (int) sizeof(sqlca->sqlstate), sqlca->sqlstate);
 		}
 
 		fflush(debugstream);
diff --git a/src/interfaces/ecpg/include/sqlca.h b/src/interfaces/ecpg/include/sqlca.h
index c5f107dd33..f0322f9191 100644
--- a/src/interfaces/ecpg/include/sqlca.h
+++ b/src/interfaces/ecpg/include/sqlca.h
@@ -51,6 +51,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/compat_informix-test_informix2.c b/src/interfaces/ecpg/test/expected/compat_informix-test_informix2.c
index fc30e3278c..1f2b023694 100644
--- a/src/interfaces/ecpg/test/expected/compat_informix-test_informix2.c
+++ b/src/interfaces/ecpg/test/expected/compat_informix-test_informix2.c
@@ -68,6 +68,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/preproc-init.c b/src/interfaces/ecpg/test/expected/preproc-init.c
index b0e04731fe..441ca1f97f 100644
--- a/src/interfaces/ecpg/test/expected/preproc-init.c
+++ b/src/interfaces/ecpg/test/expected/preproc-init.c
@@ -62,6 +62,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-array.c b/src/interfaces/ecpg/test/expected/sql-array.c
index e48f5fac67..cd2252ea30 100644
--- a/src/interfaces/ecpg/test/expected/sql-array.c
+++ b/src/interfaces/ecpg/test/expected/sql-array.c
@@ -75,6 +75,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-code100.c b/src/interfaces/ecpg/test/expected/sql-code100.c
index 4c85530a17..f410c63c7b 100644
--- a/src/interfaces/ecpg/test/expected/sql-code100.c
+++ b/src/interfaces/ecpg/test/expected/sql-code100.c
@@ -62,6 +62,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-copystdout.c b/src/interfaces/ecpg/test/expected/sql-copystdout.c
index d2599fb0e9..69f743443a 100644
--- a/src/interfaces/ecpg/test/expected/sql-copystdout.c
+++ b/src/interfaces/ecpg/test/expected/sql-copystdout.c
@@ -64,6 +64,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-declare.c b/src/interfaces/ecpg/test/expected/sql-declare.c
index 6248d99217..f515e954ee 100644
--- a/src/interfaces/ecpg/test/expected/sql-declare.c
+++ b/src/interfaces/ecpg/test/expected/sql-declare.c
@@ -70,6 +70,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-define.c b/src/interfaces/ecpg/test/expected/sql-define.c
index e97caec5b0..42f62a939e 100644
--- a/src/interfaces/ecpg/test/expected/sql-define.c
+++ b/src/interfaces/ecpg/test/expected/sql-define.c
@@ -77,6 +77,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-dynalloc.c b/src/interfaces/ecpg/test/expected/sql-dynalloc.c
index a95dddf15d..0c4efe96de 100644
--- a/src/interfaces/ecpg/test/expected/sql-dynalloc.c
+++ b/src/interfaces/ecpg/test/expected/sql-dynalloc.c
@@ -63,6 +63,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-dynalloc2.c b/src/interfaces/ecpg/test/expected/sql-dynalloc2.c
index 711857706a..db4d6b783e 100644
--- a/src/interfaces/ecpg/test/expected/sql-dynalloc2.c
+++ b/src/interfaces/ecpg/test/expected/sql-dynalloc2.c
@@ -63,6 +63,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-dyntest.c b/src/interfaces/ecpg/test/expected/sql-dyntest.c
index 513d44c630..9b3e540ff3 100644
--- a/src/interfaces/ecpg/test/expected/sql-dyntest.c
+++ b/src/interfaces/ecpg/test/expected/sql-dyntest.c
@@ -116,6 +116,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-indicators.c b/src/interfaces/ecpg/test/expected/sql-indicators.c
index 7cf43ad622..f10edb388f 100644
--- a/src/interfaces/ecpg/test/expected/sql-indicators.c
+++ b/src/interfaces/ecpg/test/expected/sql-indicators.c
@@ -64,6 +64,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-sqljson.c b/src/interfaces/ecpg/test/expected/sql-sqljson.c
index 39221f9ea5..5750b225c4 100644
--- a/src/interfaces/ecpg/test/expected/sql-sqljson.c
+++ b/src/interfaces/ecpg/test/expected/sql-sqljson.c
@@ -64,6 +64,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/sql-sqljson_jsontable.c b/src/interfaces/ecpg/test/expected/sql-sqljson_jsontable.c
index b2a0f11eb6..e586c24914 100644
--- a/src/interfaces/ecpg/test/expected/sql-sqljson_jsontable.c
+++ b/src/interfaces/ecpg/test/expected/sql-sqljson_jsontable.c
@@ -64,6 +64,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/thread-alloc.c b/src/interfaces/ecpg/test/expected/thread-alloc.c
index 3b31d27fd3..7e53368ea4 100644
--- a/src/interfaces/ecpg/test/expected/thread-alloc.c
+++ b/src/interfaces/ecpg/test/expected/thread-alloc.c
@@ -79,6 +79,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/thread-descriptor.c b/src/interfaces/ecpg/test/expected/thread-descriptor.c
index e34f4708d1..6742180087 100644
--- a/src/interfaces/ecpg/test/expected/thread-descriptor.c
+++ b/src/interfaces/ecpg/test/expected/thread-descriptor.c
@@ -75,6 +75,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
diff --git a/src/interfaces/ecpg/test/expected/thread-prep.c b/src/interfaces/ecpg/test/expected/thread-prep.c
index 052e27b634..8439126e32 100644
--- a/src/interfaces/ecpg/test/expected/thread-prep.c
+++ b/src/interfaces/ecpg/test/expected/thread-prep.c
@@ -79,6 +79,7 @@ struct sqlca_t
 	/* 7: empty						*/
 
 	char		sqlstate[5];
+	char		sqlstatepad;	/* nul terminator for sqlstate */
 };
 
 struct sqlca_t *ECPGget_sqlca(void);
#12Andres Freund
andres@anarazel.de
In reply to: Daniel Gustafsson (#11)
Re: may be a buffer overflow problem

Hi,

On 2024-06-17 23:52:54 +0200, Daniel Gustafsson wrote:

Since sqlca is, according to our docs, present in other database systems we
should probably keep it a 5-char array for portability reasons. Adding a
padding character should be fine though.

How about, additionally, adding __attribute__((nonstring))? Wrapped in an
attribute, of course. That'll trigger warning for many unsafe uses, like
strlen().

It doesn't quite detect the problematic case in ecpg_log() though, seems it
doesn't understand fprintf() well enough (it does trigger in simple printf()
cases, because they get reduced to puts(), which it understands).

Adding nonstring possibly allow us to re-enable -Wstringop-truncation, it triggers a
bunch on

../../../../../home/andres/src/postgresql/src/interfaces/ecpg/ecpglib/misc.c: In function ‘ECPGset_var’:
../../../../../home/andres/src/postgresql/src/interfaces/ecpg/ecpglib/misc.c:575:17: warning: ‘__builtin_strncpy’ output truncated before terminating nul copying 5 bytes from a string of the same length [-Wstringop-truncation]
575 | strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));

The only other -Wstringop-truncation warnings are in ecpg tests and at least
the first one doesn't look bogus:

../../../../../home/andres/src/postgresql/src/interfaces/ecpg/test/compat_oracle/char_array.pgc: In function 'main':
../../../../../home/andres/src/postgresql/src/interfaces/ecpg/test/compat_oracle/char_array.pgc:54:5: warning: '__builtin_strncpy' output truncated before terminating nul copying 5 bytes from a string of the same length [-Wstringop-truncation]
54 | strncpy(shortstr, ppppp, sizeof shortstr);

Which seems like a valid complaint, given that shortstr is a char[5], ppppp
is "XXXXX" and thatshortstr is printed:
printf("\"%s\": \"%s\" %d\n", bigstr, shortstr, shstr_ind);

Greetings,

Andres Freund

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#12)
Re: may be a buffer overflow problem

Andres Freund <andres@anarazel.de> writes:

On 2024-06-17 23:52:54 +0200, Daniel Gustafsson wrote:

Since sqlca is, according to our docs, present in other database systems we
should probably keep it a 5-char array for portability reasons. Adding a
padding character should be fine though.

How about, additionally, adding __attribute__((nonstring))? Wrapped in an
attribute, of course. That'll trigger warning for many unsafe uses, like
strlen().

What I was advocating for is that we make it *safe* for strlen, not
that we double down on awkward, non-idiomatic, unsafe coding
practices.

Admittedly, I'm not sure how we could persuade compilers that
a char[5] followed by a char field is a normal C string ...

regards, tom lane

#14Andres Freund
andres@anarazel.de
In reply to: Tom Lane (#13)
Re: may be a buffer overflow problem

Hi,

On 2024-06-17 22:42:41 -0400, Tom Lane wrote:

Andres Freund <andres@anarazel.de> writes:

On 2024-06-17 23:52:54 +0200, Daniel Gustafsson wrote:

Since sqlca is, according to our docs, present in other database systems we
should probably keep it a 5-char array for portability reasons. Adding a
padding character should be fine though.

How about, additionally, adding __attribute__((nonstring))? Wrapped in an
attribute, of course. That'll trigger warning for many unsafe uses, like
strlen().

What I was advocating for is that we make it *safe* for strlen, not
that we double down on awkward, non-idiomatic, unsafe coding
practices.

Given that apparently other platforms have it as a no-trailing-zero-byte
"string", I'm not sure that that is that clearly a win. Also, if they just
copy the field onto the stack or such, they'll have the same issue again.

And then there is this:

Admittedly, I'm not sure how we could persuade compilers that
a char[5] followed by a char field is a normal C string ...

I think the explicit backstop of a zero byte is a good idea, but I don't think
we'd just want to rely on it.

Greetings,

Andres Freund

#15Peter Eisentraut
peter@eisentraut.org
In reply to: Andres Freund (#12)
Re: may be a buffer overflow problem

On 18.06.24 04:35, Andres Freund wrote:

On 2024-06-17 23:52:54 +0200, Daniel Gustafsson wrote:

Since sqlca is, according to our docs, present in other database systems we
should probably keep it a 5-char array for portability reasons. Adding a
padding character should be fine though.

How about, additionally, adding __attribute__((nonstring))? Wrapped in an
attribute, of course. That'll trigger warning for many unsafe uses, like
strlen().

It doesn't quite detect the problematic case in ecpg_log() though, seems it
doesn't understand fprintf() well enough (it does trigger in simple printf()
cases, because they get reduced to puts(), which it understands).

See also <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115513&gt;.

Adding nonstring possibly allow us to re-enable -Wstringop-truncation,

Note that that would only work because we now always use our own
snprintf(), which is not covered by that option. I mean, we could still
do it, but it's not like the reasons we originally disabled that option
have actually gone away.