Absurdalnie zły program w C

0

Zadanie (punktowane):

Napisz program w czystym C, który wczyta linijkę tekstu o dowolnej liczbie znaków i wypisze ją.

"Rozwiązanie":

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	char* str = malloc(sizeof(char*));
	gets(str);
	puts(str);
	return 0;
}

Załóżmy, że jesteście nauczycielem akademickim, który otrzymał taką kartkówkę do sprawdzenia. Co robicie?

I jakich używacie argumentów, jak student twierdzi, że "przecież program działa": http://melpon.org/wandbox/permlink/bW16M14FclOhNgV2 ?

Disclaimer: To nie ja jestem tym nauczycielem akademickim ofc :P

0

Pokazać, że nie działa - tutaj to proste. Problemem jest jeśli ciężko będzie wykazać niedziałanie.

0
 

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

    int main(void) {

      char *str = malloc( sizeof( char* ) );
     
      while(1) {
	
	int *k = malloc( 44 );
	gets(str);
	puts(str);
	free( k );
	
      }
     
      return 0;

    }


posypie sie niemal od razu.

0

Ja spróbowałbym wytłumaczyć studentowi na czym w ogóle polega cały myk z zarządzaniem pamięcią w aplikacji (czyli co robi to śmieszne malloc), co takiego magicznego czai się pod instrukcją przypisania (z punktu widzenia procesora) i dopiero wtedy próbował wpajać, dlaczego buffer overflow naprawdę stanowi problem.

0

Tak dawno nie programowałem w C że nie do końca czaje. Ale ten malloc z tak małym size mnie troche dziwi

1

Masz (dwa) trzy wyjścia :

  • uwalenie lekko na siłe - robisz linjkę długą na jeden terabajt i testujesz (dowolna to dowolna) - (dla pewności odpal na jakimś kompie co ma mniej Ramu +Swap - atari 65 XE się nada) .
  • przepuszczenie - C i tak jest bez sensu - nie takie bugi widziałem w kodzie produkcyjnym
  • podrzucić flage: (z tego co kojarzę -D_FORTIFY_SOURCE=2)

O nawet działa (trzeba dodatkowo włączyć Optimization)

/tmp/ccd4bsws.o: In function `main':
prog.c:(.text.startup+0x17): warning: the `gets' function is dangerous and should not be used.

*** buffer overflow detected ***: ./prog.exe terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f2e0d0547e5]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c)[0x7f2e0d0f556c]
/lib/x86_64-linux-gnu/libc.so.6(+0x116570)[0x7f2e0d0f3570]
/lib/x86_64-linux-gnu/libc.so.6(__gets_chk+0x1a2)[0x7f2e0d0f3502]
./prog.exe[0x4004cb]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f2e0cffd830]
./prog.exe[0x400509]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:01 10240433                           /home/jail/prog.exe
00600000-00601000 rw-p 00000000 fd:01 10240433                           /home/jail/prog.exe
01a0d000-01a2e000 rw-p 00000000 00:00 0                                  [heap]
7f2e0cdc7000-7f2e0cddd000 r-xp 00000000 fd:01 9835306                    /opt/wandbox/gcc-head/lib64/libgcc_s.so.1
7f2e0cddd000-7f2e0cfdc000 ---p 00016000 fd:01 9835306                    /opt/wandbox/gcc-head/lib64/libgcc_s.so.1
7f2e0cfdc000-7f2e0cfdd000 rw-p 00015000 fd:01 9835306                    /opt/wandbox/gcc-head/lib64/libgcc_s.so.1
7f2e0cfdd000-7f2e0d19c000 r-xp 00000000 fd:01 5243062                    /lib/x86
0

student twierdzi, że "przecież program działa"

Bo działa, pod warunkiem że wpiszesz nie więcej niż sizeof(char*)-1 znaków ;-)

Program skompilowany GCC ma - niestety - tendencję do działania w przypadku dłuższych tekstów.
Ale, zgodnie z moim przewidywaniem, program skompilowany Visual C++ wywala się już na krótkim zdaniu:

bum.png

0

Mam pytanie: nie można użyć do napisania tego programu funkcji getchar i putchar?

0
kario97 napisał(a):

Mam pytanie: nie można użyć do napisania tego programu funkcji getchar i putchar?

Można, ale i tak taki program można skopać:
http://www.cplusplus.com/reference/cstdio/getchar/

0
vpiotr napisał(a):
kario97 napisał(a):

Mam pytanie: nie można użyć do napisania tego programu funkcji getchar i putchar?
Można, ale i tak taki program można skopać:
http://www.cplusplus.com/reference/cstdio/getchar/

Zalinkowana strona ma bardzo istotne niedomówienie, sugerujące inne niż faktyczne działanie funkcji getchar().
Jest więc niemal gwarantem skopania programu.

getchar istotnie zwraca znak pobrany z klawiatury. Ale nie czeka na naciśnięcie tego klawisza. Pobiera znak z bufora klawiatury. Jeśli bufor jest pusty, getchar czeka na wpisanie całego wiersza (zakończonego enterem) i zwraca pierwszy znak. Kolejne naciśnięcia getchar zwracają kolejne znaki z bufora, aż do jego opróżnienia.

0

Dobrze, to teraz moje podejście.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
 
const size_t str_init_length = 128;
char *str;

void free_str(void) {
  free(str);
}
 
int main(void) {
  if((str = calloc(str_init_length, 1)) == NULL) {
    return EXIT_FAILURE;
  } else {
    atexit(free_str);
  }
  size_t max_length = str_init_length; // Including nullterm
  size_t actual_length = 0; // Not including nullterm
    
  bool at_least_one_success = false;
  while(fgets( str+actual_length, max_length-actual_length, stdin) != NULL ) {
    actual_length = strlen(str);
    if(actual_length == max_length-1) {
      char *realloc_ret; if((realloc_ret=realloc(str, max_length*=2)) == NULL) {
        return EXIT_FAILURE;
      } else {
        str = realloc_ret;
      }
    }
    at_least_one_success = true;
  }
  
  if(ferror(stdin) != 0 || !at_least_one_success || puts(str) == EOF) {
    return EXIT_FAILURE;
  }
  
  return EXIT_SUCCESS;
}

http://ideone.com/uYtlLB

Czy jest poprawnie, czy skopałem?

1

realloc chyba nie gwarantuje Ci clear.
więc jak input skończy się po 128 bajcie na '\n' to wydrukujesz kawałek pamięci

EDIT : a jednak nie, jest git -> komentarz

1 użytkowników online, w tym zalogowanych: 0, gości: 1