*** stack smashing detected ***: <unknown> terminated [RESOLVIDO]

1. *** stack smashing detected ***: <unknown> terminated [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 05/11/2018 - 23:04h

Olá, boa madrugada!
Então, recetemente andei tentando me adaptar ao IPv6 em programação de sockets em C, porém está sendo...um pouco complicado para mim. Muitos 'warnings', vazamento de memoria pra todo lado e outras coisas ruins. O código mais recente que escrevi foi um simples servidor para trabalha com IPV4 e IPV6. Com IPv4 ele funciona de boa, porém o mesmo não acontece com IPv6.

Segue o código:


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

#include <netdb.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int make_server_socket(void){

int sockfd;
struct addrinfo *res=NULL, hints;

memset(&hints, 0, sizeof(struct addrinfo));

hints.ai_flags=AI_PASSIVE;
hints.ai_family=AF_INET6;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;

if(getaddrinfo(NULL, "9009", &hints, &res)!=0){

printf("\n* getaddrinfo() -> failed\n");
exit(1);

}else{

struct addrinfo *it;

for(it=res; it!=NULL; it=it->ai_next){

if(it->ai_family==AF_INET || it->ai_family==AF_INET6){

sockfd=socket(it->ai_family, SOCK_STREAM, IPPROTO_TCP);

if(sockfd<0){

continue;

}else{

if(bind(sockfd, it->ai_addr, it->ai_addrlen)==0){

break;
}
}

close(sockfd);
}
}

freeaddrinfo(res);
}

return sockfd;
}

void show_addr(const struct sockaddr *addr){

char strAddr[64];

if(addr->sa_family==AF_INET){

struct sockaddr_in *addr4=(struct sockaddr_in*)addr;

inet_ntop(AF_INET, &addr4->sin_addr.s_addr,
strAddr, INET_ADDRSTRLEN);

printf("[ipv4 addr] -> %s\n", strAddr);

}else if(addr->sa_family==AF_INET6){

struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;

inet_ntop(AF_INET6, addr6->sin6_addr.s6_addr,
strAddr, INET6_ADDRSTRLEN);

printf("[ipv6 addr] -> %s\n", strAddr);

}else{

printf("\n* Unknown address family\n"); //essa parte não faz
//muito sentido!
}
}

int main(void){

int sockfd=make_server_socket();

if(sockfd<0){

printf("\n* make_server_socket() -> failed\n");
exit(1);
}

if(listen(sockfd, 10)<0){

printf("\n* listen() -> failed\n");
exit(1);
}

size_t max;
int csockfd;
struct sockaddr addr;
socklen_t socklen=sizeof(struct sockaddr);

printf("Enter the maximum number of connections >");
scanf("%ld", &max);

for(size_t i=0; i<max; i++){

csockfd=accept(sockfd, &addr, &socklen);

if(csockfd!=-1){

show_addr(&addr);

send(csockfd, "Bonjour!\0", 8, 0);

close(csockfd);
}
}

close(csockfd);

return 0;
}


Execução (aceitando conexões IPV4):


zherkezhi@zherkezhi:~/Documents/C$ ./server

Enter the maximum number of connections >5

[ipv4 addr] -> 127.0.0.1
[ipv4 addr] -> 127.0.0.1
[ipv4 addr] -> 127.0.0.1
[ipv4 addr] -> 127.0.0.1
[ipv4 addr] -> 127.0.0.1


Ok! Funcionou como o previsto com IPv4.

Execução (aceitando conexões IPV6):


zherkezhi@zherkezhi:~/Documents/C$ ./server

Enter the maximum number of connections >5

[ipv6 addr] -> ::8064:a4fb:fe7f:0
[ipv6 addr] -> ::1
[ipv6 addr] -> ::1
[ipv6 addr] -> ::1
[ipv6 addr] -> ::1

*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)



OBS: Estava usando como cliente o NC (netcat).

Por que está dando esse "*** stack smashing detected ***"? Qual o problema de trabalhar com IPv6?

E aproveitando a pergunta: Dá onde saiu aquele primeiro endereço Ipv6 (::8064:a4fb:fe7f:0) sendo que eu estava sempre usando o endereço local (::1)?


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 06/11/2018 - 11:00h

zherkezhi escreveu:

    struct sockaddr addr;
socklen_t socklen=sizeof(struct sockaddr);

/* (...) */

csockfd=accept(sockfd, &addr, &socklen);


Suspeito que o problema esteja aqui. Veja o seguinte programa e seu resultado.
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>

int main(void){
printf(
"sizeof(struct sockaddr): %zd\n"
"sizeof(struct sockaddr_in): %zd\n"
"sizeof(struct sockaddr_in6): %zd\n",
sizeof(struct sockaddr),
sizeof(struct sockaddr_in),
sizeof(struct sockaddr_in6)
);
}
sizeof(struct sockaddr): 16
sizeof(struct sockaddr_in): 16
sizeof(struct sockaddr_in6): 28


Como você pode ver, a estrutura addr declarada no seu programa original não é suficiente para acomodar o endereço preenchido pela função accept() quando a conexão é por IPv6.

Na primeira chamada a accept(), o valor socklen vai como 16, de modo que apenas os primeiros 16 bytes do ponteiro indicado por addr são preenchidos com dados da conexão (os outros 12 são truncados), mas o valor de socklen é alterado para 28, para indicar o tamanho real do endereço. Quando você manda imprimir o endereço, a função de impressão percebe que o tipo do endereço é AF_INET6, de modo que ele se comporta como se a estrutura apontada tivesse realmente 28 bytes, usando os primeiros 16 bytes realmente ocupados pela estrutura, e mais 12 bytes de coisas que estão na memória em volta dela (que são dados da pilha da função main, dentro da qual a estrutura de dados original foi declarada).

A partir da segunda chamada a accept(), você reaproveita o valor de socklen, que agora está como 28. Contudo, a estrutura que vai conter o endereço continua com apenas 16 bytes, de modo que a função accept() acaba sobrescrevendo 12 bytes que estão em volta (antes ou depois, dependendo de detalhes da arquitetura; nos nossos PCs, geralmente é depois) do espaço alocado para addr. Ao fazer isso, você corrompe o conteúdo do que estava guardado nesses 12 bytes, conteúdo esse que provavelmente inclui o stack frame e o endereço de retorno da função main().

Finalmente, quando a função main() acaba de executar, o procedimento de encerrar a função encontra uma pilha corrompida, com endereço de retorno inconsistente, que provoca a falha no programa.


Como corrigir isso?

O mais legal seria pode alterar a função accept(), para que em lugar de um ponteiro ela recebesse um ponteiro para ponteiro, e ela mesma alocasse um ponteiro do tipo e do tamanho correto, de acordo com a conexão. Como, contudo, isso não é possível, temos de seguir outro caminho.

A primeira coisa a fazer é não cair na tentação de reaproveitar o valor de socklen, mas redefini-lo antes de cada chamada a accept() (eu nunca tinha prestado atenção a isso — foi até bom, para mim mesmo, ver o erro no seu programa: assim também aprendo).

O passo seguinte é alocar um buffer de tamanho suficiente para qualquer tipo de socket, e usar conversão de tipos ao invocar accept().
char addr[1024];
/* ... */
socklen=sizeof addr;
csockfd=accept(sockfd, (struct sockaddr *)addr, &socklen);
if(csockfd!=-1){
show_addr((struct sockaddr *)addr);
/* ... */
}
/* ... */


O uso de um ponteiro auxilar poderia abreviar as múltiplas conversões de tipo.
char addr_buf[1024];
/* ... */
struct sockaddr *addr=(struct sockaddr *)addr_buf;
socklen=sizeof addr_buf;
csockfd=accept(sockfd, addr, &socklen);
if(csockfd!=-1){
show_addr(addr);
/* ... */
}
/* ... */


Outra possibilidade, evitando alocar mais bytes do que o necessário, é usar union, valendo-se também do fato de que o endereço de cada membro da união e o mesmo endereço da união como um todo (não sei se essa solução, que eu não testei, é mais “limpa” ou mais “suja”, mas creio que funciona).
union any_sockaddr {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_un sun;
struct sockaddr_ipx sipx;
/* etc. */
};

/* ... */

void show_addr(const union any_sockaddr &addr){
switch(addr->sock.sa_family){
case AF_INET:
/* Usa addr->sin */
break;
case AF_INET6:
/* Usa addr->sin6 */
break;
case AF_LOCAL:
/* Usa addr->sun */
break;
case AF_IPX:
/* Usa addr->sipx */
break;
/* etc. */
default:
/* Usa addr->sa */
break;
}
}

/* ... */

int main(void)
/* ... */
union any_sockaddr addr;
socklen=sizeof addr;
csockfd=accept(sockfd, &addr.sa, &socklen);
if(csockfd!=-1){
show_addr(&addr);
/* ... */
}
/* ... */
}



3. Re: *** stack smashing detected ***: <unknown> terminated [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 06/11/2018 - 22:33h

Resolveu o problema?


4. RESOLVIDO

Perfil removido
removido

(usa Nenhuma)

Enviado em 07/11/2018 - 09:30h

paulo1205 escreveu:

Resolveu o problema?


Sim Paulo, muito obrigado!








Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts