This is my code:
#include <stdio.h>
int main(void) {
int n;
fprintf(stdout, "Hello%n World\n", &n);
fprintf(stdout, "n: %d\n", n);
return 0;
}
This is my output:
Hellon: 0
Why does the fprintf format specifier "%n" not work?
Why is the string to be printed interrupted?
ISO/IEC 9899:201x C11 - 7.21.6.1 - The fprintf function
The conversion specifiers and their meanings are:
(...)
%n The argument shall be a pointer to signed integer into which is written the number of characters written to the output stream so far
by this call to fprintf. No argument is converted, but one is
consumed. If the conversion specification includes any flags, a field
width, or a precision, the behavior is undefined. ...
(...)
This is my compiler version used on Code::Blocks:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=C:/Program\ Files/mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev
0/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-8.1.0/configure --host=x86_64-w64-mingw32 --bu
ild=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysr
oot=/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64 --enable-shared --enable
-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdc
xx-time=yes --enable-threads=posix --enable-libgomp --enable-libatomic --enable-
lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --
enable-version-specific-runtime-libs --disable-libstdcxx-pch --disable-libstdcxx
-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls
--disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=noco
na --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/p
rerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/x86
_64-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/x86_64-w64-mingw32-s
tatic --with-isl=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-pkgv
ersion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=https:/
/sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x
86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x
86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/
include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6
-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include
-I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS=' -I/c/
mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prere
quisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw
32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/x86_64-810-posix-seh-
rt_v6-rev0/mingw64/opt/lib -L/c/mingw810/prerequisites/x86_64-zlib-static/lib -L
/c/mingw810/prerequisites/x86_64-w64-mingw32-static/lib '
Thread model: posix
gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
As documented in the Microsoft documentation, the %n is disabled by default in the Microsoft C library used on your MinGW system:
Important
Because the %n format is inherently insecure, it is disabled by default. If %n is encountered in a format string, the invalid parameter handler is invoked, as described in Parameter Validation. To enable %n support, see _set_printf_count_output.
Whether %n is actually unsafe as claimed by Microsoft is highly debatable. The examples shown to support this claim combine this printf function with the use of a variable format string that can by changed by the attacker via a buffer overflow error.
On some Microsoft systems (but maybe not the latest), you could fix your program this way:
#include <stdio.h>
int main(void) {
int n;
_set_printf_count_output(1);
fprintf(stdout, "Hello%n World\n", &n);
fprintf(stdout, "n: %d\n", n);
return 0;
}
For a more portable approach, here is a work around to avoid using %n and still get the same results:
#include <stdio.h>
int main(void) {
int n;
n = fprintf(stdout, "Hello");
fprintf(stdout, " World\n");
fprintf(stdout, "n: %d\n", n);
return 0;
}
Output:
Hello World
n: 5
Related
I can compile and run a program that assigns a long int literal, albeit it one that would fit into an int, to an int variable.
$ cat assign-long-to-int.c
#include <stdio.h>
int main(void){
int i = 1234L; //assign long to an int
printf("i: %d\n", i);
return 0;
}
$ gcc assign-long-to-int.c -o assign-long-to-int
$ ./assign-long-to-int
i: 1234
I know that 1234 would fit into an int but would still expect to be able to enable a warning. I've been through all the gcc options but can't find anything suitable.
Is it possible to generate a warning for this situation? From the discussion here, and the gcc options, the short answer is no. It isn't possible.
Would there be any point in such a warning?
It's obvious in the trivial example I posted that 1234L is being assigned to an int variable, and that it will fit. However, what if the declaration and the assignment were separated by many lines of code? The programmer writing 1234L is signaling that they expect this literal integer to be assigned to a long. Otherwise, what's the point of appending the L?
In some situations, appending the L does make a difference. For example
$ cat sizeof-test.c
#include <stdio.h>
void main(void){
printf("%ld\n", sizeof(1234));
printf("%ld\n", sizeof(1234L));
}
$ ./sizeof-test
4
8
Although the compiler must know that 1234L would fit into a 4 byte int, it puts it into an 8 byte long.
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.3.0-17ubuntu1~20.04' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-HskZEa/gcc-9-9.3.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
Compilers should check the value range, not the type of the integer constant. Otherwise we would end up with a lot of whining whenever we initialize a small integer type, since there are no small integer constants smaller than int.
short i = 32768; does for example yield a warning with clang -Wconstant-conversion but not with gcc. There's -Wconversion but it's prone to false positives on either compiler.
If you want to guard against implicit conversions between various integer types, you should probably use a static analyser instead.
In the case of constants, the compiler can see that the value in question fits into the type being assigned to, so there's really no point in warning. If the constant was out of range, i.e. 5000000000L, then the compiler will see that and generate a warning.
What the compiler can do however is warn when an integer type that is not a compile type constant is assigned to a lower type:
long y = 1;
int x = y;
If you add the -Wconversion flag (not included in either -Wall or -Wextra), you'll get this warning:
x1.c:6:5: warning: conversion to ‘int’ from ‘long int’ may alter its value [-Wconversion]
int x = y;
The compiler will automatically convert between most primitive integer types. When you convert from a larger type to a smaller type, I'm pretty sure its a feature of the C language that the number will be truncated.
For example, the following code will print "0xef":
#include <stdio.h>
#include <stdint.h>
int main() {
uint32_t x = 0xdeadbeef;
uint8_t y = x;
printf("0x%x\n", y);
return 0;
}
To address your question specifically, I don't think there is a warning for this behavior, because this conversion is technically a defined feature of the C language.
All:
I have a very simple C test code using the Intel compiler to do some timing for a large loop for float point operation, the code (test.c) is as follows:
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <omp.h>
int main(char *argc, char **argv) {
const long N = 1000000000;
double t0, t1, t2, t3;
double sum=0.0;
clock_t start, end;
struct timeval r_start, r_end;
long i;
gettimeofday(&r_start, NULL);
start = clock();
for (i=0;i<N;i++)
sum += i*2.0+i/2.0; // doing some floating point operations
end = clock();
gettimeofday(&r_end, NULL);
double cputime_elapsed_in_seconds = (end - start)/(double)CLOCKS_PER_SEC;
double realtime_elapsed_in_seconds = ((r_end.tv_sec * 1000000 + r_end.tv_usec)
- (r_start.tv_sec * 1000000 + r_start.tv_usec))/1000000.0;
printf("cputime_elapsed_in_sec: %e\n", cputime_elapsed_in_seconds);
printf("realtime_elapsed_in_sec: %e\n", realtime_elapsed_in_seconds);
//printf("sum= %4.3e\n", sum);
return 0;
}
However when I tried to compile and run it with Intel 13.0 compiler, the large loop seems to be ignored and the execution resulted in zero timing:
$ icc test.c
$ ./a.out
cputime_elapsed_in_sec: 0.000000e+00
realtime_elapsed_in_sec: 9.000000e-06
Only if I print the sum (uncomment line 26), the loop will actually be executed:
$ icc test.c
$ ./a.out
cputime_elapsed_in_sec: 2.730000e+00
realtime_elapsed_in_sec: 2.736198e+00
sum= 1.250e+18
The question is why the loop seems not executed if I do not print the sum value?
The same issue does not occur with gcc-4.4.7 compilers, I guess the intel compiler might have done some optimization that if the variable is not referenced, the loop is probably ignored?
The system information is as follows:
$ uname -a
Linux node001 2.6.32-642.11.1.el6.x86_64 #1 SMP Wed Oct 26 10:25:23 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux
$ icc -v
icc version 13.0.0 (gcc version 4.4.7 compatibility)
$ gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
Thanks for any suggestions!
Roy
Given your observation that printing the final value slows it down(a), there's a fairly good chance that the optimiser is figuring out that you're not actually using sum for anything after you've calculated it, so it's optimising the entire calculation loop out of existence.
I actually saw something similar quite a while ago when we were testing the performance of the latest VAX 11/780 machine our university had received (showing my age there). It was faster by a factor of several thousand percent for exactly the same reason, the new optimising compiler having decided that the loop wasn't actually needed.
To be certain, you'd have to examine the assembly output. I believe this can be done with icc by using the -Fa <asmFileName> option and then examining the file whose name you used in place of <asmFileName>.
(a) The other possibility I thought of seems to be discounted here.
That was the possibility that, given the range of i is constant (based on N) and that the calculation otherwise involves constants, it may be that the compiler itself had calculated the final value while compiling it, resulting in a simple constant load operation.
I've seen gcc do this sort of thing at its -O3 "insane" optimisation level.
I discount that possibility since the printing of the value would most likely not affect this operation.
The output of the following piece of code (considering %No as a string),
#include <stdio.h>
int main(void) {
// your code goes here
printf("%No");
return 0;
}
on a linux machine is: %No
while on a windows machine is: 13
For the following code (considering %No as a format specifier), output
#include <stdio.h>
int main(void) {
// your code goes here
printf(" %c %No %d",65,65,23);
return 0;
}
on a linux machine is: A %No 23
while on a windows machine is: A 101 23
The output on the windows machine keeps on varying with different arguments for %No specifier. Any explanation about this specifier would be very helpful.
Thanks in Advance.
output of gcc -v on my windows machine
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=c:/program files (x86)/codeblocks/mingw/bin/../libexec/gcc/mingw32/4.7.1/lto-wrapper.exe
Target: mingw32
Configured with: ../../src/gcc-4.7.1/configure --build=mingw32 --enable-languages=c,c++,ada,fortran,objc,obj-c++ --enable-threads=win32 --enable-libgo
mp --enable-lto --enable-fully-dynamic-string --enable-libstdcxx-debug --enable-version-specific-runtime-libs --with-gnu-ld --disable-nls --disable-wi
n32-registry --disable-symvers --disable-build-poststage1-with-cxx --disable-werror --prefix=/mingw32tdm --with-local-prefix=/mingw32tdm --enable-cxx-
flags='-fno-function-sections -fno-data-sections' --with-pkgversion=tdm-1 --enable-sjlj-exceptions --with-bugurl=http://tdm-gcc.tdragon.net/bugs
Thread model: win32
gcc version 4.7.1 (tdm-1)
EDIT:
Got my answer, as cremno pointed out, %N does nothing, and %o prints in octal, so in %No , N is ignored and %o is used to print the passed argument as octal. The remaining question is why passing no argument is taken as 11(in decimal). FYI in ASCII 11 represents a vertical tab.
You have mismatched printf format string and arguments; that results in undefined behaviour. Anything could happen. If you want to print a literal %, use %% in the format string.
For a homework assignment, I have to try to use buffer overflows to crash a simple c program. My problem is that my compiler won't compile the gets() function because it is deprecated and unsafe. I understand this, but for the sake of the example, I'd like to override that. Here is the code:
#include <stdio.h>
int main(int argc, char *argv[]) {
int valid = 0;
// Char arrays w/buffer set to 8 chars
char str1[8];
char str2[8];
next_tag(str1);
//This is where I want to use gets and not fgets or other secure functions...
gets(str2);
if(strncmp(str1, str2, 8) == 0) {
valid = 1;
}
// Print
printf("Buffer 1: str1(%s), str2(%s), valid(%d)\n", str1, str2, valid);
}
My gcc version is:
rabbitfighter#ECHO:[~/Code/C/BufferOverflowExamples]: gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.1/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /build/gcc/src/gcc-4.9-20140903/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-cloog-backend=isl --disable-isl-version-check --disable-cloog-version-check --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --disable-multilib --disable-werror --enable-checking=release
Thread model: posix
gcc version 4.9.1 20140903 (prerelease) (GCC)
If anyone can help me I would greatly appreciate it. I am running Manjaro Linux (Arch).
You can use a "safer" funcion like fgets and give it a bogus buffer size to get unsafe behavior out of it.
A larger buffer size than the real buffer will let fgets overrun its bounds and potentially crash the program.
Just because the gun has a safety doesn't mean you can't shoot your foot off with it.
How do I use the GNU C Library version of basename() and dirname()?.
If you
#include <libgen.h>
for dirname
You're already getting the POSIX, not the GNU, version of basename(). (Even if you
#define _GNU_SOURCE
As far as I know there is no conditional importing in C. Is there a gcc specific trick?
Just write it yourself and give it a different name than basename. This GNU insistence on creating alternate non-conforming versions of standard functions that can be written in 1-3 lines is completely batty.
char *gnu_basename(char *path)
{
char *base = strrchr(path, '/');
return base ? base+1 : path;
}
This way, your program will also be more portable.
According to the man page you should do
#define _GNU_SOURCE
#include <string.h>
If you get the POSIX version, libgen.h is probably already included before that point. You may want to include -D_GNU_SOURCE in the CPPFLAGS for compilation:
gcc -D_GNU_SOURCE ....
Compare: POSIX Version vs GNU Version on Compiler Explorer.
After examining libgen.h, I'm pretty sure I have a warning-free and error-free solution:
/* my C program */
#define _GNU_SOURCE /* for GNU version of basename(3) */
#include <libgen.h> /* for dirname(3) */
#undef basename /* (snide comment about libgen.h removed) */
#include <string.h> /* for basename(3) (GNU version) and strcmp(3) */
/* rest of C program... */
With the #undef line, now my program includes dirname(3) from libgen.h and the GNU version of basename(3) from string.h.
No compiler warnings/errors from either gcc (version 4.5.2) or clang (version 3.3).
Make sure you're building with the GNU C library, rather than your system's (presumed) POSIX-compatible default.
This is often set in the GCC spec file. Use the -v option to show the current settings:
$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
It's crazy basename and dirname have two versions.
We worked at a big project, it looks like these two apis already caused
potentially bugs. So we marked "basename" "dirname" as deprecated for warning if
someone use it:
#ifdef basename
__attribute__ ((deprecated))
char *__xpg_basename(char *path);
#else
__attribute__ ((deprecated))
char *basename(const char *path);
#endif
__attribute__ ((deprecated))
char *dirname(char *path);
We also try to introduce a base c foundation library such as glib or libcork,
but it looks like too heavy. So we write a tiny library for this purpose, it
implementation like this:
#include <libgen.h> // for dirname
#include <linux/limits.h> // for PATH_MAX
#include <stdio.h> // for snprintf
#include <string.h> // for basename
#include <stdbool.h> // for bool
bool get_basename(const char *path, char *name, size_t name_size) {
char path_copy[PATH_MAX] = {'\0'};
strncpy(path_copy, path, sizeof(path_copy) - 1);
return snprintf(name, name_size, "%s", basename(path_copy)) < name_size;
}
bool get_dirname(const char *path, char *name, size_t name_size) {
char path_copy[PATH_MAX] = {'\0'};
strncpy(path_copy, path, sizeof(path_copy) - 1);
return snprintf(name, name_size, "%s", dirname(path_copy)) < name_size;
}
Then we replace all basename dirname call with get_basename get_dirname.