I have a compilation problem with gcc.
Assume the following program:
#include <stdio.h>
int test(const char *fname) {
FILE *fh = fopen(fname, "rb");
int tmp;
if (fread(&tmp, sizeof(tmp), 1, fh) < 1) {
tmp = 0;
}
fclose(fh);
return tmp;
}
int main(void) {
printf("%d\n", test("test.txt"));
return 0;
}
And the file test.txt:
11111111111111111111111111111111...
Of course, the program is very stupid, but it works:
user#ubuntu:~/tmp/optxx$ gcc -O3 -Wall -Wextra test.c
user#ubuntu:~/tmp/optxx$ ./a.out
825307441
Let's modify it a bit (only add an attribute to the test-function):
#include <stdio.h>
int __attribute__((optimize("O0"))) test(const char *fname) {
FILE *fh = fopen(fname, "rb");
int tmp;
if (fread(&tmp, sizeof(tmp), 1, fh) < 1) {
tmp = 0;
}
fclose(fh);
return tmp;
}
int main(void) {
printf("%d\n", test("test.txt"));
return 0;
}
The function test should no longer be optimized. But now compilation fails:
user#ubuntu:~/tmp/optxx$ gcc -Wall -Wextra -O3 test.c
In file included from /usr/include/stdio.h:936:0,
from test.c:1:
In function ‘fread’,
inlined from ‘test’ at test.c:6:9:
/usr/include/x86_64-linux-gnu/bits/stdio2.h:293:9: warning: call to ‘__fread_chk_warn’ declared with attribute warning: fread called with bigger size * nmemb than length of destination buffer
return __fread_chk_warn (__ptr, __bos0 (__ptr), __size, __n, __stream);
^
I get a warning and normally i compile with -Werror, so i don't like warnings.
The used gcc version:
user#ubuntu:~/tmp/optxx$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.3.1-6ubuntu2' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --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 5.3.1 20160119 (Ubuntu 5.3.1-6ubuntu2)
Unfortunately i am not able to fix the warning:(
Maybe someone has an idea why this happens?
-edit-
A (dirty) hack would also be okay to remove this warning:)
-edit-
Probably something with my linux is wrong, because it seems to work for everyone else. Anayway a dirty hack / fix would be to add this code before the fread call:
static size_t fread_wrapper(void *ptr, size_t size, size_t count, FILE *stream) {
return fread(ptr, size, count, stream);
}
#define fread(ptr, size, count, stream) fread_wrapper((ptr), (size), (count), (stream))
Ubuntu enable the _FORTIFY_SOURCE feature by default for optimization levels -O1 or higher. This option makes the system headers pre-process to use different functions for fread among others. Those functions perform some basic safety checks of arguments. The option gets enabled globally when the header is included so it makes the assumption that all the code will be compiled with -O1 or higher. I assume that it is disabled on lower optimization levels because gcc doesn't have enough information without some optimizations and that causes those magic fortify macros to generate false positives (like you got).
If you compile the whole file with -O0, _FORTIFY_SOURCE is disabled and things will work. Alternatively I guess you can compile the whole file with -D_FORTIFY_SOURCE=0, although I haven't tried it.
Also, I could reproduce this on other flavors of linux by just adding -D_FORTIFY_SOURCE=1.
I guess you can call it a compiler/glibc/Ubuntu bug. Or just stop using crazy optimization attributes. The world can't be tested with all possible weird combinations so we should exercise caution when pushing buttons and turning knobs.
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.
This question already has answers here:
literal constant vs variable in math library
(4 answers)
Do math functions of constant expressions get pre-calculated at compile time?
(6 answers)
Closed 4 years ago.
Using gcc test.c, the first code sample compiles, while the second does not. Why?
They both work with explicit linking of the math library (i.e. gcc test.c -lm.
First Sample:
#include <stdio.h>
#include <math.h>
int main () {
printf("%lf\n", sqrt(4.0) );
return 0;
}
Second Sample:
#include <math.h>
#include <stdio.h>
double sqrt2(double a) { return sqrt(a); }
int main() {
printf("%lf\n", sqrt(4.0));
printf("%lf\n", sqrt2(4.0));
return 0;
}
Linker error with second sample:
/tmp/ccuYdso7.o: In function `sqrt2':
test.c:(.text+0x13): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
gcc -v:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/8.1.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/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 --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-mul
Thread model: posix
gcc version 8.1.0 (GCC)
There is no linker error for uses of sqrt in the main function. Is there any particular reason that this is the case?
I also checked with clang, but neither compiled (linker errors) without the -lm.
gcc is a particularly clever compiler.
It will optimise out sqrt(4.0) as a compile time evaluable constant expression. It can do that since the definition of sqrt is defined by the standard and its return value is solely a function of its input. (Note futher that under IEEE754, sqrt must return the closest double to the final result. This further supports the optimisation hypothesis.)
In the second case, the presence of function (which could be used by other translation units) defeats this optimisation.
Since the argument to sqrt is known at compile time, and its behaviour is standardised, sqrt(4.0) can be calculated at compile time.
I tried to run an example program from a book called 'The Linux Programming Interface'. I copied all user-defined header files and functions from official website of book to my booklib location.
When I compiled the program, I took these errors. I need help about'Undefined reference to [functions_name]**.
code:
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>
#include "tlpi_hdr.h"
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[])
{
int inputFd, outputFd, openFlags;
mode_t filePerms;
ssize_t numRead;
char buf[BUF_SIZE];
if(argc != 3 || strcmp(argv[1], "--help") == 0)
{
usageErr("%s old-file new-file\n", argv[0]);
}
//open input old-file
inputFd = open(argv[1], O_RDONLY);
//error check
if(inputFd == -1)
{
errExit("opening file %s", argv[1]);
}
openFlags = O_CREAT | O_WRONLY | O_TRUNC;
filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
//open output the new-file
outputFd = open(argv[2], openFlags, filePerms);
if(outputFd == -1)
{
errExit("opening file %s", argv[2]);
}
//transfer data until we encounter end of input or an error
while((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
{
if(write(outputFd, buf, numRead) != numRead)
fatal("couldn't write whole buffer");
if(numRead == -1)
errExit("read");
if(close(inputFd) == -1)
errExit("close input");
if(close(outputFd) == -1)
errExit("close output");
}
exit(EXIT_SUCCESS);
}
You can see user-defined header files from here.
$gcc -I booklib -o copy copy.c
c
/tmp/ccqC9Tg9.o: In function `main':
copy.c:(.text+0x62): undefined reference to `usageErr'
copy.c:(.text+0xb3): undefined reference to `errExit'
copy.c:(.text+0x11c): undefined reference to `errExit'
copy.c:(.text+0x14b): undefined reference to `fatal'
copy.c:(.text+0x163): undefined reference to `errExit'
copy.c:(.text+0x183): undefined reference to `errExit'
copy.c:(.text+0x1a3): undefined reference to `errExit'
collect2: error: ld returned 1 exit status
gcc -v output:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-6' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --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 --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --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 6.3.0 20170205 (Debian 6.3.0-6)
For the undefined references, someone asked a very similar answer here How to compile examples in the book "The Linux Programming Interface" by Michael Kerrisk
Then, if that does not work, I would suggest to try again by downloading the source file as specified at the TLPI web site and you can look into the makefile for your script, which is in the fileio/ subfolder and the Makefile.inc file.
I was having the exact same issues with undefined references to his error-handling functions, but if you're doing everything in your own directories my advice may not help. If you downloaded the compressed tarball from his website everything should work. As stated in the BUILDING.txt file it includes, all you have to do is run make from the tlpi-dist or tlpi-book directory, depending on which one you downloaded, and all programs will be compiled and ready to run (subject to some OS specific issues obviously). If you want to modify a program, just add the name you save it as to that directory's Makefile and run make again, and you should be good to go.
I was having similar troubles on Ubuntu 16.04. and MacOSX.
Running 'make' always gave errors containing:
No rule to make target `../libtlpi.a'
This has a simple solution for Ubuntu:
sudo apt-get install libcap-dev
then run 'make' in the root directory.
You would require to ../lib/error_functions.o file along with your compilation.
gcc -I booklib -o copy copy.c ../lib/error_functions.o
The error functions are defined and declared in lib/error_functions.c lib/error_function.h
Make sure you run make from tlpi-dist by which you would have error_functions.o
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.
I executed the below C program which prints a the primes below the given N (N < 4294967295). It went fine when executed in SunOS, but I'm getting Segmentation fault (core dumped) when running in Ubuntu(compiled it with gcc compilter). Can anyone please let me know where I went wrong. Mentioned below the compiler versions of SunOS and Ubuntu 12.10
cc -V
cc: Sun C 5.9 SunOS_sparc Patch 124867-01 2007/07/12
gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.7.2-2ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --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.7.2 (Ubuntu/Linaro 4.7.2-2ubuntu1)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<inttypes.h>
int main()
{
FILE *fpin,*fpout;
char ch[11], file_name[100];
long long int num1=0,i,tmp=0;
long long int *arr;
printf("enter file name:: ");
gets(file_name);
fpin = fopen(file_name,"r");
fpout = fopen("/home/code/output.c","w");
while(1)
{
fgets(ch,11,fpin);
if (!feof(fpin))
{
num1=atoll(ch);
arr = prime_number(num1);
for(i=0;*(arr+i)!='\0';i++)
{
fprintf(fpout,"%llu",*(arr+i));
if(*(arr+i+1) == '\0')
fputc('.',fpout);
else
fputc(',',fpout);
}
}
else
{
fclose(fpin);
fclose(fpout);
break;
}
}
}
prime_number(long long int n)
{
long long int i,j,total=0,a[200];
int count=0;
printf("\n%llu \n",n);
for (j=2;j<=n;j++)
{
count = 0;
for (i=1;i<=j;i++)
{`enter code here`
if ((j%i) == 0)
count++;
if (count > 2)
break;
}
if (count==2)
{
a[total] = j;
total++;
}
}
return(&a[0]);
}
At the end of your prime_number funciton, you are returning a pointer to a variable which is defined in (the stack of) the prime_number function itself. Since when the function exists, the contents of its stack are effectivley vaporized, you are returning an invalid pointer.
Of course, depending on what system, OS, etc you are running, the contents of the stack may have not been immediatley overwritten and would APPEAR valid for a short period of time, but this is just luck.
Try either mallocing a return result (and freeing with your caller), or passing the array TO the prime_number function. This way, the contents will remain valid for the caller.
return(&a[0]);
You're returning a pointer to a local variable, which doesn't exist after your function returns.
If you run gcc with warnings, your compiler should tell you this.