SlideShare uma empresa Scribd logo
Prevenc¸˜ao de Ataques em Sistemas Distribu´ıdos via An´alise de
Intervalos
Vitor Mendes Paisante, Luiz Felipe Zafra Saggioro, Raphael Ernani Rodrigues,
Leonardo Barbosa Oliveira, Fernando Magno Quint˜ao Pereira
1
Departamento de Ciˆencia da Computac¸˜ao – UFMG
Av. Antˆonio Carlos, 6627 – 31.270-010 – Belo Horizonte – MG – Brazil
{paisante,luizfzsaggioro,raphael,leob,fernando}@dcc.ufmg.br
Abstract. The range analysis of integer variables determines the lowest and
highest bounds that each variable assumes throughout the execution of a pro-
gram. This technique is vital to detect a plethora of software vulnerabilities
but the literature does not describe any principled way to apply range analy-
sis on distributed systems. This negligence is unfortunate, as networks are the
most common targets of software attacks. The goal of this paper is to set right
this omission. Capitalizing on a recent algorithm to infer communication proto-
cols, we have designed, implemented and tested a range analysis for distributed
systems. Our contribution, a holistic view of the system, is more precise than
analyzing each system module independently. In this paper we support this sta-
tement through a number of examples, and experiments performed on top of
the SPEC CPU 2006 benchmarks. A prototype of our tool, implemented on the
LLVM compiler, is available for scrutiny.
Resumo. A an´alise de largura de vari´aveis determina o maior e menor valores
que cada vari´avel inteira de um programa pode assumir durante a sua execuc¸˜ao.
Tal t´ecnica ´e de suma importˆancia para detectar vulnerabilidades em programas
mas, at´e o momento, n˜ao existe abordagem que aplique essa an´alise em siste-
mas distribu´ıdos. Tal omiss˜ao ´e s´eria, uma vez que esse tipo de sistema ´e alvo
comum de ataques de software. O objetivo deste artigo ´e preencher tal lacuna.
Valendo-nos de um algoritmo recente para inferir protocolos de comunicac¸˜ao,
n´os projetamos, implementamos e testamos uma an´alise de largura de vari´aveis
para sistemas distribu´ıdos. Nosso algoritmo, ao prover uma vis˜ao hol´ıstica do
sistema distribu´ıdo, ´e mais preciso que analisar cada parte daquele sistema se-
paradamente. Demonstramos tal fato via uma s´erie de exemplos e experimentos
realizados sobre os programas presentes em SPEC CPU 2006. Um prot´otipo
de nossa ferramenta, implementado sobre o compilador LLVM, est´a dispon´ıvel
para escrut´ınio.
1. Introduc¸˜ao
A an´alise de largura de intervalos [Cousot and Cousot 1977], ´e uma das t´ecnicas mais
importantes que compiladores usam para encontrar vulnerabilidades em programas. Essa
an´alise determina, para cada vari´avel inteira usada em um programa, quais s˜ao o menor e
o maior valores que ela pode assumir. Tal informac¸˜ao permite ao compilador detectar a
possibilidade de ocorrˆencia de dois fenˆomenos que comprometem a seguranc¸a de progra-
mas. O primeiro deles ´e o acesso fora de limites de arranjos. Esse evento ocorre quando
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
209 c 2014 SBC — Soc. Bras. de Computação
uma vari´avel inteira i indexa um enderec¸o inv´alido a partir de um ponteiro base a. Erros
assim s˜ao comuns em linguagens fracamente tipadas, como C, uma vez que a express˜ao
a[i] n˜ao assegura que i seja menor que o maior enderec¸o dereferenci´avel a partir de a.
O segundo fenˆomeno que a an´alise de largura de vari´aveis descobre estaticamente s˜ao os
estouros de inteiros. Uma operac¸˜ao como j = i+1, em linguagens como C, C++ ou Java,
pode retornar um valor j menor que i, se i for o maior inteiro represent´avel. Por raz˜oes
que discutiremos na sec¸˜ao 4, essa semˆantica pode levar a vulnerabilidades de software.
A an´alise de largura de vari´aveis existe h´a quase 40 anos. Desde a
sua concepc¸˜ao original, em 1977 [Cousot and Cousot 1977], v´arios desafios re-
lacionados `a implementac¸˜ao dessa an´alise foram superados, tanto em termos
de precis˜ao [Gawlitza et al. 2009, Su and Wagner 2005], quanto em termos de
eficiˆencia [Logozzo and Fahndrich 2008]. Recentemente, por exemplo, cientistas de-
monstraram como propagar informac¸˜oes de largura de vari´aveis em estruturas de da-
dos [Oh et al. 2011], eliminando um dos ´ultimos entraves ao projeto de an´alises de grande
precis˜ao. Entretanto, pesquisadores ainda n˜ao haviam abordado a an´alise de largura de
vari´aveis em programas distribu´ıdos. O presente artigo trata desta abordagem.
O grande empecilho `a an´alise de sistemas distribu´ıdos devia-se a um fato sim-
ples: at´e pouco tempo atr´as n˜ao havia m´etodo confi´avel para determinar, estaticamente,
quais operac¸˜oes de envio e recepc¸˜ao de mensagens se comunicam. Em outras palavras,
uma vez que operac¸˜oes como receive, que coleta mensagens da rede, eram conside-
radas inseguras, pouco se podia assumir quanto aos valores coletados, j´a visto que eles
podem provir de quaisquer fontes. Entretanto, esse problema foi superado por Teixeira et
al. [Teixeira et al. 2014] neste ano de 2014. Teixeira et al. desenvolveram um algoritmo
que infere canais de comunicac¸˜ao entre programas que integram um sistema distribu´ıdo.
Valendo-nos de tal m´etodo, n´os projetamos, implementamos e testamos um algoritmo que
propaga informac¸˜oes de largura de vari´aveis entre n´os que se comunicam em uma rede.
Nossa soluc¸˜ao para o problema da an´alise de largura de vari´aveis em sistemas
distribu´ıdos consiste em cinco passos. (i) N´os determinamos quais dados representam
as mensagens que um programa manipula. (ii) N´os aplicamos a an´alise de largura de
vari´aveis nesses dados, para determinar o layout das mensagens do programa. (iii) Usando
as t´ecnicas de Teixeira et al., n´os determinamos quais os canais de comunicac¸˜ao existem
entre os programas distribu´ıdos. (iv) N´os emparelhamos as mensagens trocadas por esse
canal, para determinar quais dados est˜ao fluindo de um programa para o outro. (v) N´os
executamos a an´alise de largura de vari´aveis uma segunda vez, agora sobre todo o sistema
distribu´ıdo, obtendo resultados finais. Dentre esses cinco passos, somente (iii) n˜ao ´e uma
contribuic¸˜ao original deste artigo. Enfatizamos que os passos (i) e (ii) descobrem n˜ao
somente o layout de mensagens, mas o layout de arranjos em geral. O fato de podermos
entender, de forma autom´atica, como campos est˜ao dispostos em mensagens ´e uma con-
sequˆencia dessa generalidade. Nossa ferramenta foi implementada sobre o compilador
LLVM [Lattner and Adve 2004], e est´a dispon´ıvel publicamente.
2. Contextualizac¸˜ao
A fim de ilustrar a importˆancia do problema abordado neste artigo, usaremos os dois pro-
gramas vistos na figura 1. O c´odigo mostrado na parte (a) da figura lˆe uma quantidade N de
caracteres da entrada padr˜ao, e os envia atrav´es de uma conex˜ao de rede, para o programa
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
210 c 2014 SBC — Soc. Bras. de Computação
send(1);
ack = recv()
if (ack == 13) {
N = getc();
send(N);
i = 0;
while (i < N) {
s = getc();
send(s);
ack = recv();
if (ack != 17) {
break;
} else {
s = getc();
i++;
}
}
send('0');
}
msg = recv();
if (msg == 1) {
send(13);
size = recv();
j = 0;
buf = malloc(size);
while (true) {
c = recv();
if (msg != '0')
send(17);
buf[j] = c;
j++;
else
break;
}
} else {
send(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 15
16
17
18
16
17
18
19
(a) (b)
Figura 1. Canais impl´ıcitos de comunicac¸ ˜ao em programa distribu´ıdo.
na figura 1 (b). O programa (b) recebe esses caracteres e os armazena em um arranjo de
tamanho N. Caso fosse poss´ıvel escrever mais que N caracteres no arranjo usado no pro-
grama (b), ent˜ao observar´ıamos a ocorrˆencia de um fenˆomeno chamado buffer overflow.
Esse ´e um dos principais meios que atacantes usam para comprometer o funcionamento
de programas. O nosso programa exemplo, contudo, ´e seguro: o programa (a) nunca
transmite para o programa (b) mais que N bytes de dados. Entretanto, a an´alise individual
do programa (b) n˜ao nos permite inferir tal fato: na ausˆencia de maiores informac¸˜oes,
um analisador est´atico deve, conservadoramente, assumir que o lac¸o na linha 7 daquele
programa pode executar mais que N iterac¸˜oes.
Cada operac¸˜ao de send que se comunica com uma instruc¸˜ao recv cria um ca-
nal de comunicac¸˜ao impl´ıcito. A definic¸˜ao de todos os canais impl´ıcitos em um sistema
distribu´ıdo ´e um problema indecid´ıvel. Existem, contudo, algoritmos que apontam a pos-
sibilidade de existˆencia de tais canais de forma relativamente precisa. Um deles foi recen-
temente proposto por Teixeira et al.. Os poss´ıveis canais de comunicac¸˜ao impl´ıcitos que
esse algoritmo encontra aparecem indicados pelas setas tracejadas na figura 1. Analisando
esses canais, pode-se concluir que as vari´aveis N e size possuem o mesmo valor.
3. Arcabouc¸o de An´alises Est´aticas
Nossa an´alise distribu´ıda de largura de vari´aveis segue a sequˆencia de passos mostrada
na figura 2. A an´alise de segmentac¸˜ao infere os diferentes campos que constituem cada
mensagem trocada em um sistema distribu´ıdo. A an´alise local de intervalos nos per-
mite encontrar qual informac¸˜ao est´a armazenada em cada campo de uma mensagem. A
propagac¸˜ao de informac¸˜oes nos diz quais dados fluem de um programa para outro, efe-
tivamente habilitando a ´ultima fase de nossa abordagem: a an´alise global de intervalos.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
211 c 2014 SBC — Soc. Bras. de Computação
Análise de
Segmentação
(seção 3.1)
Análise
Local de
Intervalos
(seção 3.1)
Inferência de
Formato de
Mensagens
(seção 3.1)
Propagação
de Informação
(seção 3.2)
Análise
Global de
Intervalos
(seção 3.3)
Figura 2. An´alises est´aticas que comp˜oem o nosso arcabouc¸o de inferˆencia
de largura de vari´aveis em sistemas distribu´ıdos. Todas essas etapas s˜ao
contribuic¸ ˜oes deste artigo.
Cada uma dessas an´alises ´e discutida nas pr´oximas sec¸˜oes deste artigo.
3.1. Inferˆencia Autom´atica de Formato de Mensagens
O objetivo deste trabalho ´e portar uma an´alise de largura de vari´aveis tradicional para
um sistema distribu´ıdo. Para alcanc¸ar esse objetivo, o primeiro desafio que precisamos
vencer ´e como entender o formato das mensagens trocadas via rede. Normalmente, uma
mensagem ´e implementada como um arranjo. Algumas c´elulas desses arranjos s˜ao agru-
padas em campos. Diferentes programas encadeiam esses campos de diferentes maneiras.
Campos de mensagens podem conter, por exemplo, seu opcode1
, o identificador do reme-
tente, um contador de tempo (costumeiramente conhecido como time-stamp), e dados. A
figura 3 (a) ilustra um programa que cria um dentre dois tipos diferentes de mensagens, e
envia a mensagem criada atrav´es de uma operac¸˜ao send.
An´alise Local de Intervalos. Para inferir como informac¸˜oes s˜ao passadas entre n´os que
se comunicam via rede, utilisamos a mesma an´alise de largura de vari´aveis que queremos
portar para o mundo distribu´ıdo. Por´em, dessa vez n´os executamos tal an´alise localmente,
isto ´e, de forma individual para cada programa que faz parte do sistema distribu´ıdo. A
an´alise de largura de vari´aveis local nos d´a, para cada vari´avel inteira, uma func¸˜ao R,
definida da seguinte maneira:
R(v) = [l, u], {l, u} ⊂ Z ∪ {−∞, +∞}, l ≤ u
Como existem v´arias implementac¸˜oes de an´alises de intervalos descritas na li-
teratura [Cousot and Cousot 1977, Gawlitza et al. 2009, Rodrigues et al. 2013,
Su and Wagner 2005], n´os omitiremos os detalhes do algoritmo que infere a func¸˜ao
R automaticamente. Ao leitor interessado, recomendamos o trabalho de Rodrigues et
al. [Rodrigues et al. 2013], que descreve uma implementac¸˜ao eficiente de tal an´alise.
Assumiremos, portanto, a existˆencia de uma t´ecnica para construir a func¸˜ao R, que nos
informa, para cada vari´avel v, uma estimativa do menor e do maior valores que v assume
durante a execuc¸˜ao de um programa.
An´alise de Segmentac¸˜ao. De posse da func¸˜ao R, passamos `a segunda fase de nossa
t´ecnica. Nessa etapa, nosso objetivo ´e inferir para cada ponteiro p uma tabela de seg-
mentos que o descreva. A tabela de segmentos associada a um ponteiro p ´e uma lista
de intervalos que podem ser usados para indexar partes de p. Continuando com o nosso
exemplo, a figura 3 (b) mostra os intervalos que podem ser usados para indexar o ar-
ranjo apontado pelo ponteiro a. Os diferentes ponteiros usados para carregar dados em
1
O opcode de uma mensagem ´e um valor que descreve o tipo daquela mensagem, e permite ao seu
receptor escolher a forma de tratamento mais adequado para ela.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
212 c 2014 SBC — Soc. Bras. de Computação
a = malloc(40);
if (first_msg) {
a1 = a + 0;
*a1 = ACK; // ack is 1
a2 = a + 1;
for (i = 0; i < 4; i++) {
a3 = a2 + i;
*a3 = id >> i;
}
a4 = a + 5;
for (i = 0; i < 35; i++) {
a5 = a4 + i;
*a5 = getc() % 100;
}
a6 = a + 39;
*a6 = '0';
} else {
a7 = a + 0;
*a7 = EOF; // eof is 7
a8 = a + 1;
for (i = 0; i < 39; i++) {
a9 = a8 + i;
*a9 = -1;
}
}
send(a);
a1
a2
a4
a6
a3
a5
a7
a9
a8 a3 ! a9
a5 ! a9
a6 ! a9(a) (b) (c)
a1 ! a7{a1, a7}
{a2, a3, a8, a9}
{a3, a9}
{a4, a5, a9}
{a5, a9}
{a6, a9} (d)
Figura 3. (a) Exemplo de programa que cria e envia mensagens. (b) Os intervalos
dos v´arios ponteiros que podem indexar blocos na mensagem. (c) Outra vis˜ao
dos intervalos de ponteiros, agrupados por ´area comum de indexac¸ ˜ao. (d) A
tabela de segmentos da mensagem, formada pelos diferentes ponteiros usados
para efetivamente armazenar dados nela via operac¸ ˜oes de carregamento.
a = malloc(v) ⇒ T(a) = [0, u], sendo R(v) = [l, u]
a′
= a + c ⇒



R(a′
) = [min(l + c, l′
), max(u + c, u′
)],
sendo R(a) = [l, u], R(a′
) = [l′
, u′
]
T(a) = T(a′
)
∗a = x ⇒ join(T(a), R(a))
Figura 4. Equac¸ ˜oes usadas para encontrar os segmentos que constituem cada
arranjo de um programa.
um arranjo determinam a sua tabela de segmentos. Assim, a tabela de segmentos para
o ponteiro a de nosso exemplo pode ser vista na figura 3 (d). A tabela de segmentos T
associada a um arranjo a ´e constru´ıda de acordo com as equac¸˜oes mostradas na figura 4.
A func¸˜ao merge, usada para tratar operac¸˜oes de carregamento, e.g., ∗a = x,
´e definida na figura 5. Nessa implementac¸˜ao, n´os usamos a sintaxe de ML, uma
linguagem de programac¸˜ao funcional. Tabelas s˜ao representadas como listas de tu-
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
213 c 2014 SBC — Soc. Bras. de Computação
fun assertContiguous [] = true
| assertContiguous [_] = true
| assertContiguous ((l1, u1, _) :: (l2, u2, p) :: r) =
u1 = l2 andalso assertContiguous ((l2, u2, p) :: r)
fun merge [] (l, u, p) = [(l, u, [p])]
| merge ((ll, uu, pp)::r) (l, u, p) =
if l > uu
then (ll, uu, pp) :: merge r (l, u, p)
else if l = ll
then if u < uu
then (ll, u, p::pp) :: (u, uu, pp) :: r
else (ll, uu, p::pp) :: merge r (uu, u, p)
else if u < uu
then (ll, l, pp) :: (l, u, p::pp) :: (u, uu, pp) :: r
else (ll, l, pp) :: (l, uu, p::pp) :: merge r (uu, u, p)
fun join t (l, u, p) = if assertContiguous t
then merge t (l, u, p)
else nil
join [(0, 39, [a])]
(0, 1, a1)
= [ (0,1,[a1,a]),(1,39,[a]) ]
join [ (0,1,[a1,a]),(1,39,[a]) ]
(1, 5, a3)
= [ (0,1,[a1,a]),(1,5,[a3,a]),
(5,39,[a]) ]
join [ (0,1,[a1,a]),(1,5,[a3,a]),(5,39,[a]) ]
(1, 5, a9)
= [ (0,1,[a1,a]),(1,5,[a9,a3,a]),
(5,39,[a]) ]
join [ (0,1,[a1,a]),(1,5,[a9,a3,a]),
(5,39,[a]) ]
(5, 38, a5)
= [ (0,1,[a1,a]),(1,5,[a9,a3,a]),
(5,38,[a5,a]),(38,39,[a]) ]
Figura 5. Algoritmo que faz o emparelhamento de tabelas de ponteiros. `A direita
do algoritmo mostramos algumas chamadas da func¸ ˜ao join para os ponteiros
vistos na figura 3 (a).
plas. Cada tupla possui trˆes elementos, e.g., (l, u, pp). Os inteiros l e u represen-
tam o in´ıcio e o final do segmento. A lista pp guarda todos os ponteiros que s˜ao
usados para indexar aquele segmento. O operador ::, em ML, denota a concatenac¸˜ao
de listas. Assim, a tabela vista na figura 3 (c) ´e representada pela seguinte notac¸˜ao:
[(0, 1, [a1, a7]), (1, 5, [a3, a9]), (5, 38, [a5, a9]), (38, 39, [a6, a9])]. Note que os segmentos
determinam uma classe de equivalˆencia sobre a tabela. Em outras palavras, cada c´elula
de um arranjo pertence a um segmento e a intersec¸˜ao de dois segmentos diferentes sem-
pre ´e vazia. Essas propriedades implicam em contig¨uidade, isso ´e, o intervalo final de
um segmento ´e o intervalo inicial de seu vizinho. N´os salientamos essa propriedade via a
func¸˜ao assertContiguous, a qual pode ser vista na figura 5. A func¸˜ao join recebe
uma tabela t e um intervalo para ser inserido em t. Verificada a contig¨uidade de t, join
modifica t via uma invocac¸˜ao de merge, para que ela passe a conter o novo segmento.
As diversas possibilidades de modificac¸˜ao s˜ao vistas na parte direita da figura 5.
Qu˜ao precisa ´e nossa an´alise de segmentac¸˜ao? Neste artigo, estamos interessados em
descobrir o layout de arranjos usados como mensagens em sistemas distribu´ıdos. Por ou-
tro lado, a nossa an´alise de segmentac¸˜ao ´e mais geral: ela descobre segmentos em qual-
quer arranjo usado em um programa. Assim, podemos mensurar a precis˜ao da an´alise
contando o n´umero de segmentos descobertos por arranjo: quanto mais segmentos desco-
brirmos por arranjo, mais precisa ´e nossa an´alise de segmentac¸˜ao. A figura 6 mostra esse
n´umero para os arranjos nos programas de SPEC CPU 2006. Essa tabela inclui todos os
arranjos encontrados naquele benchmark. Essa abrangˆencia nos d´a uma id´eia muito me-
lhor da precis˜ao de nossa an´alise, que se restring´ıssemos esse experimento somente aos
arranjos trocados como mensagens em sistemas distribu´ıdos, pois esses seriam poucos.
A figura 6 conta o n´umero de tabelas de segmentos, em vez do n´umero de arran-
jos, pois a mesma tabela pode ser formada por arranjos diferentes. Isso acontece quanto
a an´alise de ponteiros de LLVM n˜ao consegue dizer se dois ponteiros podem ou n˜ao refe-
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
214 c 2014 SBC — Soc. Bras. de Computação
Benchmark #Tabelas #m´edio de segmentos Maior tabela %Arranjos
433.milc 631 3 34 85.00%
444.namd 617 4 44 72.00%
447.dealII 9,843 2 109 75.00%
450.soplex 2,784 2 55 89.00%
470.lbm 24 16 152 99.00%
401.bzip2 493 2 92 93.00%
429.mcf 103 3 19 68.00%
456.hmmer 4,872 2 64 86.00%
458.sjeng 356 2 14 96.00%
462.libquantum 343 2 6 99.00%
464.h264ref 15,436 2 45 97.00%
471.omnetpp 2,518 2 50 58.00%
473.astar 333 3 22 95.00%
483.xalancbmk 25,194 2 62 82.00%
Total/M´edia/Max/M´edia 4,539 3.4 152 85.29%
Figura 6. Precis˜ao de nossa an´alise de segmentac¸ ˜ao.
renciar o mesmo enderec¸o base. Pela figura 6, vemos que, em m´edia, cada tabela possui
3.4 segmentos. A maior tabela que observamos, presente em lbm, possui 152 segmen-
tos. Em outras palavras, essa tabela representa um arranjo indexado por 152 vari´aveis
contendo intervalos de valores diferentes. A figura 6 cont´em uma coluna %Arranjos, que
descreve a porcentagem de arranjos que pudemos analisar por benchmark. Somente anali-
samos arranjos cujo ponteiro base ´e conhecido. Ou seja, precisamos encontrar, no c´odigo
do programa, o ponto de criac¸˜ao do arranjo. Arranjos alocados por func¸˜oes externas2
, por
exemplo, n˜ao podem ser analisados.
Inferˆencia de Intervalos em Campos de Mensagens. Feita a segmentac¸˜ao da mem´oria
nos programas que formam o sistema distribu´ıdo, passamos `a fase de inferˆencia de valores
em mensagens. Nessa etapa, estamos interessados em encontrar quais os intervalos de
valores que podem ser armazenados em cada segmento. Fazemos isso via o lac¸o abaixo:
• Para cada instruc¸˜ao de carregamento ∗a = x presente no programa:
– Para cada segmento s que cont´em a in T(a) fac¸a
∗ R(s) = R(s) ∪ R(x)
A figura 7 mostra o resultado da inferˆencia de mensagens quando aplicada no
programa visto na figura 3 (a). Inicialmente, todos os segmentos da tabela associada ao
arranjo a cont´em intervalos inteiros indefinidos, indicados pela notac¸˜ao [?, ?]. Durante o
processamento das instruc¸˜oes de carregamento, os valores desconhecidos s˜ao substitu´ıdos
pelos valores encontrados via a an´alise local de intervalos. Se duas ou mais instruc¸˜oes,
tais como ∗a = x1 e ∗a = x2, carregam valores nos mesmos segmentos, ent˜ao esse
segmento recebe a uni˜ao dos intervalos R(x1) ∪ R(x2). Esse fenˆomeno pode ser visto
durante o processamento da instruc¸˜ao ∗a7 = EOF, na figura 7, que expande o segmento
associado `a {a5, a9}, de [0, 99] para [−1, 99].
2
Uma func¸˜ao ´e externa se o seu c´odigo fonte n˜ao est´a dispon´ıvel para nosso compilador.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
215 c 2014 SBC — Soc. Bras. de Computação
*a1 = ACK
*a3 = id >> 1
*a5 = getc % 100
*a6 = '0'
*a7 = EOF
*a9 = 0
{a1,a7}
{a3,a9}
{a5,a9}
{a6,a9}
[1, 1]
[0, +!][1, 1]
[0, +!][1, 1] [0, 99]
[0, +!][1, 1] [0, 99] [0, 0]
[0, +!][1, 7] [0, 99] [0, 0]
["1, +!][1, 7] ["1, 99] ["1, 0]
[?, ?]
[?, ?]
[?, ?]
[?, ?]
[?, ?]
[?, ?]
Figura 7. Inferˆencia de intervalos inteiros na mensagem vista na figura 3.
b = recv();
b1 = b + 0;
msg_type = *b1;
if (msg_type == ACK) {
b1 = b + 5;
c = malloc(35);
for (j = 0; j < 35; i++) {
b2 = b1 + j;
tmp = *b2;
c1 = c + j;
*c1 = tmp;
}
printf(c1);
} else {
printf("End of Stream");
}
[1, 7]
[0, +!]
["1, 99]
["1, 0]
b
["1, 99]
["1, 0]
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Figura 8. A esquerda, vˆe-se um trecho de c´odigo que recebe mensagens envi-
adas pelo programa da figura 3 (a). `A direita, vˆeem-se mensagens recuperadas
via o emparelhamento por canais impl´ıcitos.
A inferˆencia de valores de mensagens termina t˜ao logo todas as instruc¸˜oes de
carregamento no programa sejam processadas. Cada instruc¸˜ao ´e visitada somente uma
vez, ent˜ao a terminac¸˜ao ´e garantida. Assim, a complexidade computacional dessa an´alise
´e proporcional ao n´umero de instruc¸˜oes de carregamento no programa.
3.2. Propagac¸˜ao de Informac¸˜ao entre Programas
Finda a fase local de nosso algoritmo, que envolve os trˆes passos descritos na sec¸˜ao 3.1,
passamos `a fase distribu´ıda de nossa an´alise. Nessa etapa, tabelas de segmentos em pro-
gramas diferentes s˜ao emparelhadas, de acordo com os canais de comunicac¸˜ao inferidos
usando-se o algoritmo de Teixeira et al.. Para cada canal de comunicac¸˜ao inferido, as tabe-
las enviadas s˜ao emparelhadas com as tabelas recebidas. A figura 8 ilustra esse processo.
O programa visto nessa figura recebe mensagens enviadas pelas instruc¸˜oes mostradas na
figura 3 (a). Uma vez que existe um canal de comunicac¸˜ao impl´ıcito entre a instruc¸˜ao
send da figura 3 (a) e a instruc¸˜ao recv da figura 8, temos que as tabelas associadas aos
arranjos a e b devem ser emparelhadas.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
216 c 2014 SBC — Soc. Bras. de Computação
A partir de um canal de comunicac¸˜ao impl´ıcito formado por uma operac¸˜ao de
send s e uma operac¸˜ao de recv r, podemos definir as tabelas de segmentos origem e des-
tino. Chamamos de tabela origem aquela associada a qualquer arranjo passado para s e
destino a tabela associada a algum arranjo recebido por r. Dados esses conceitos, o empa-
relhamento de tabelas de segmentos ´e uma operac¸˜ao simples, e consiste no casamento de
campos cujos ´ındices se correspondem nas tabelas origem e destino. A figura 8 mostra as
tabelas de segmentos associadas aos arranjos b e c, as quais obtemos via emparelhamento
com a tabela origem associada ao arranjo a da figura 3 (a).
3.3. An´alise Global de Largura de Vari´aveis
Terminado o emparelhamento, comec¸amos a ´ultima fase da abordagem que esse artigo
prop˜oe: a an´alise global de intervalos. Essa etapa n˜ao requer qualquer algoritmo especi-
almente adaptado para o universo dos sistemas distribu´ıdos. Para encontrar os intervalos
associados `as vari´aveis inteiras de cada programa que integra o sistema, podemos usar
qualquer algoritmo j´a descrito na literatura. As informac¸˜oes necess´arias ao correto funci-
onamento do algoritmo j´a foram inferidas nos passos anteriores. Essa flexibilidade ´e uma
das vantagens de nossa abordagem.
Continuando com o nosso exemplo, n´os temos que a vari´avel tmp, inicializada
na linha 9 da figura 8, pode conter somente valores dentro do intervalo [−1, 99]. Esse
intervalo foi inferido para segmentos apontados pelo ponteiro b2 no passo de propagac¸˜ao
de informac¸˜ao entre n´os comunicantes. Caso analis´assemos o programa da figura 8 em
separado, ter´ıamos de assumir que a vari´avel tmp pudesse ser inicializada com qualquer
valor inteiro. Essa perda de precis˜ao deve-se ao fato de uma an´alise individual n˜ao nos
dar qualquer informac¸˜ao sobre dados recebidos via operac¸˜oes de recv.
4. Estudo de Caso
N´os implementamos nossa an´alise sobre o compilador LLVM, vers˜ao 3.3, pois tanto o
trabalho de Teixeira et al. quanto a an´alise de largura de vari´aveis de Rodrigues et al.
foram constru´ıdas nesse compilador. A fim de demonstrar o funcionamento da t´ecnica
proposta neste artigo, esta sec¸˜ao descreve sua utilizac¸˜ao sobre um par cliente-servidor
real. O c´odigo presente aqui pode ser compilado e testado diretamente3
. O cliente usado
neste estudo de caso envia para um servidor uma quantidade indeterminada de pares for-
mados por nomes de funcion´arios e horas trabalhadas. As mensagens que carregam essas
informac¸˜oes possuem dois campos. O servidor, ao receber cada um desses pares, mul-
tiplica a quantidade de horas trabalhadas por um valor de sal´ario-base e envia de volta
para o cliente uma tripla, formada pelo nome do funcion´ario, suas horas trabalhadas e
seu sal´ario. A figura 9 mostra o c´odigo de nossa aplicac¸˜ao cliente, e a figura 10 mostra
o c´odigo de nossa aplicac¸˜ao servidora. Por simplicidade, neste exemplo operaremos so-
mente a n´ıvel de bytes. Assim, nomes s˜ao cadeias de bytes, horas trabalhadas s˜ao um byte
e o sal´ario-base ´e um byte tamb´em.
A figura 9 mostra, al´em do programa cliente, o protocolo de comunicac¸˜ao de
nosso estudo de caso. O cliente inicialmente informa ao servidor a quantidade de
3
Devido a restric¸˜oes de espac¸o, n˜ao mostramos as diretivas #include em nosso c´odigo. Assim, os
seguintes arquivos devem ser inclu´ıdos em cada programa: stdio.h, string.h, sys/socket.h e
arpa/inet.h.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
217 c 2014 SBC — Soc. Bras. de Computação
int main() {
int i, num, salario_calc, descriptor, ack = 0;
char buffer[42], *message, server_reply[2000];
struct sockaddr_in server;
descriptor = socket(AF_INET , SOCK_STREAM , 0);
server.sin_addr.s_addr = inet_addr("localhost");
server.sin_family = AF_INET;
server.sin_port = htons(2000);
connect(descriptor, (struct sockaddr *)&server , sizeof(server));
// Informa ao servidor quantas mensagens serao enviadas:
fscanf(stdin, "%d", &num);
buffer[0] = num < 100 ? num : 0;
send(descriptor, buffer, 1, 0);
recv(descriptor, &ack, sizeof(int), 0);
if (ack) {
// Envia os pares: (nome do trabalhador x horas trabalhadas):
for (i = 0; i < num; i++) {
// Leia o nome do trabalhador:
fscanf(stdin, "%s", buffer);
// Leia o numero de horas trabalhadas:
fscanf(stdin, "%d", &buffer[40]);
// Permite-se no maximo dez horas-extras trabalhadas:
buffer[40] = buffer[40] > 10 ? 10 : buffer[40];
// Envia a mensagem:
send(descriptor, buffer, strlen(buffer) * sizeof(char), 0);
// Recebe o valor a ser pago ao trabalhador:
recv(descriptor, buffer, strlen(buffer) * sizeof(char), 0);
salario_calc = (int)buffer[41];
printf("Valor a ser pago = %dn", salario_calc);
}
}
return 0;
}
Cliente Servidor
[num]
[1]
["nome"*, horas]
["nome"*, horas, sal]
["nome"*, horas]
["nome"*, horas, sal]
.
.
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Figura 9. (Esquerda) Programa cliente. (Direita) Protocolo de comunicac¸ ˜ao.
int main() {
int num, i, salario, descriptor, connection, clilen;
char buffer[42];
struct sockaddr_in server, client;
descriptor = socket(AF_INET , SOCK_STREAM , 0);
server.sin_addr.s_addr = inet_addr("localhost");
server.sin_family = AF_INET;
server.sin_port = htons(2000);
bind(descriptor, (struct sockaddr *)&server, sizeof(server));
connection = accept(descriptor, (struct sockaddr *)&client, &clilen);
// Recebe o numero de mensagens que vao chegar:
recv(connection, &num, sizeof(int), 0);
// Envia um ack para o cliente:
buffer[0] = 1;
send(connection, buffer, 1, 0);
// Leia todas as mensagens:
for (i = 0; i < num; i++) {
recv(connection, &buffer, strlen(buffer)*sizeof(char), 0);
salario = HORA * buffer[40]; // HORA == 12
buffer[41] = salario;
// Envia o salario final para o cliente:
send(connection, buffer, strlen(buffer)*sizeof(char), 0);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[!", +"][0, 99]
411
[!", +"][1, 1]
411
[!", +"]
40
[!", +"][!", 10]
1 1
[!", +"]
40
[!", 120][!", 10]
1 126
Figura 10. Programa servidor que interage com o cliente visto na figura 9. Os
segmentos de mensagens que nossa an´alise infere automaticamente s˜ao mos-
trados `a direita do programa.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
218 c 2014 SBC — Soc. Bras. de Computação
mensagens trocadas. Essa primeira mensagem ´e seguida de uma confirmac¸˜ao de re-
cebimento, por parte do servidor. A partir desse ponto, o cliente passa a enviar os
pares (nomes × horas) para o servidor, que lhe envia de volta as triplas (nomes ×
horas × sal´ario). Esse protocolo possui quatro canais impl´ıcitos de comunicac¸˜ao4
:
(i) cliente : send : 15 → servidor : recv : 13, (ii) servidor : send : 16 →
cliente : recv : 16, (iii) cliente : send : 27 → servidor : recv : 19 e (iv)
servidor : send : 23 → cliente : recv : 29. O algoritmo proposto por Teixeira et
al. descobre esses quatro canais e somente eles. Note que nesse caso, o algoritmo n˜ao
reporta falsos-positivos: todos os canais inferidos s˜ao, de fato, canais v´alidos.
O algoritmo proposto na sec¸˜ao 3.1 descobre os formatos de mensagens vistos `a
direita da figura 10. Nesse exemplo, nosso algoritmo foi capaz de inferir, de forma pre-
cisa, os intervalos de valores associados a cinco dos dez campos de mensagens trocadas
entre cliente e servidor. Por exemplo, o programa cliente possui um teste na linha 25 (fi-
gura 9) que garante que trabalhadores n˜ao podem fazer mais que 10 horas extra. Esse teste
permite-nos determinar que somente valores no intervalo [−∞, 10] podem ser transferidos
na posic¸˜ao 40 das mensagens. Valores imprecisos, associados aos intervalos [−∞, +∞],
devem-se `a pouca informac¸˜ao dispon´ıvel no c´odigo fonte do programa. S˜ao imprecisos,
por exemplo, os valores dos bytes associados a nomes de funcion´arios. Esses bytes podem
cobrir qualquer intervalo entre [−128, 127], exatamente o dom´ınio do tipo char.
4.1. Como utilizar nossa an´alise para aumentar a seguranc¸a e a eficiˆencia de
programas distribu´ıdos.
Estamos, atualmente, usando os resultados de nossa an´alise para eliminar guardas sobre
operac¸˜oes que podem causar estouro em aritm´etica de inteiros. Linguagens como C, C++
ou Java tratam operac¸˜oes inteiras segundo uma semˆantica modular. Se o resultado de
uma instruc¸˜ao inteira for maior que o tamanho do registrador onde esse resultado ser´a
armazenado, ent˜ao os bits mais significativos desse valor s˜ao descartados. Por exemplo,
6char × 22char = −124char . A literatura cont´em v´arias descric¸˜oes de ataques baseados
nesse semˆantica [Brumley et al. 2007, Dietz et al. 2012].
Um ataque baseado em estouro de arranjos ´e poss´ıvel em nosso estudo de caso.
Considere, por exemplo, que um usu´ario malicioso informe um valor negativo de horas
na linha 23 do programa cliente (vari´avel buffer[40] na figura 9). Suponhamos que
tal valor seja o inteiro negativo −75. Temos ent˜ao que o teste na linha 25 do programa
cliente ´e falso. Consequentemente −75 ser´a transmitido para o servidor. No c´odigo do
servidor (figura 10), a multiplicac¸˜ao na linha 20, e.g., −75char ×12char = 124char , produz
um valor maior que o m´aximo n´umero de horas cuja intenc¸˜ao do desenvolvedor seria
permitir, isto ´e, 10char × 12char = 120char . Esse tipo de falha de seguranc¸a ´e dif´ıcil de
ser detectado sem o aux´ılio de ferramentas de an´alise est´atica, e pode levar a situac¸˜oes
catastr´oficas. A t´ıtulo de exemplo, em 1996, o foguete Ariane 5 foi perdido devido a um
estouro de inteiros – tal erro de software custou ao programa espacial europeu cerca de
US$ 370 milh˜oes [Dowson 1997].
Existem diversas t´ecnicas para sanear programas contra estouros de operac¸˜oes
inteiras. Recentemente, por exemplo, Rodrigues et al. propuseram um gerador de c´odigo
que instrumenta operac¸˜oes inteiras em um programa. Essa instrumentac¸˜ao invoca c´odigo
4
N´umeros ao lado do nome do programa denotam linhas nas figuras 9 e figuras 10.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
219 c 2014 SBC — Soc. Bras. de Computação
salario = HORA * buffer[40];
int tmp0 = (int) HORA;
int tmp1 = (int) buffer[40];
if (tmp0 * tmp1 != HORA * buffer[40])
handleOverflow("Linha 19 - char", HORA, buffer[40]);
Figura 11. Exemplo de c´odigo para verificar se houve estouro de inteiro
de tratamento de erros sempre que um estouro ´e detectado. Tal t´ecnica, quando aplicada
ao programa da figura 10, insere guardas na soma da linha 18, e na multiplicac¸˜ao da
linha 20. Cada uma dessas guardas ´e implementada como uma combinac¸˜ao de testes
condicionais e instruc¸˜oes de desvio, conforme mostrado na figura 11.
Essas guardas tornam o programa instrumentado mais lento que o programa ori-
ginal. Conforme reportado por Dietz et al., essa lentid˜ao pode comprometer at´e 15%
do tempo de execuc¸˜ao do programa modificado [Dietz et al. 2012]. Nossa t´ecnica nos
permite eliminar alguns desses testes, e tamb´em indicar ao desenvolver quais testes preci-
sam ser mantidos. Por exemplo, considerando-se o programa servidor, visto na figura 10,
nossa an´alise elimina o teste sobre o incremento realizado na linha 18, pois a vari´avel
i ´e limitada por num, cujo intervalo superior pode ser no m´aximo 99. Por outro lado,
n˜ao podemos eliminar a guarda da multiplicac¸˜ao da linha 20, pois buffer[40], caso
fosse um n´umero negativo muito pequeno, causaria um estouro aritm´etico. Nossa an´alise
detecta tamb´em essa possibilidade e mant´em a instrumentac¸˜ao na linha 20.
5. Trabalhos Relacionados
O presente trabalho relaciona-se a pesquisa desenvolvida tanto em an´alise de c´odigo,
quanto em sistemas distribu´ıdos. No primeiro caso, nossa inspirac¸˜ao mais importante
deve-se a Cousot e Cousot [Cousot and Cousot 1977], que introduziram o conceito de
an´alise de largura de vari´aveis. No segundo caso, contudo, nossa inspirac¸˜ao ´e bem mais
recente: muito do que discutimos neste artigo foi poss´ıvel somente devido ao arcabouc¸o
constru´ıdo por Teixeira et al.. No restante dessa sec¸˜ao discutiremos como nosso trabalho
se relaciona com outras pesquisas nesses dois campos.
An´alise de largura de vari´aveis. A an´alise de largura de vari´aveis ´e um dos exem-
plos cl´assicos de interpretac¸˜ao abstrata. A t´ecnica de interpretac¸˜ao abstrata, introdu-
zida por Cousot e Cousot, ´e um arcabouc¸o te´orico que permite a compiladores obter
informac¸˜oes de um programa, garantindo que os algoritmos usados terminam. Existem
muitas formas de se implementar an´alise de largura de intervalos [Gawlitza et al. 2009,
Mahlke et al. 2001, Stephenson et al. 2000, Su and Wagner 2005]. Essas t´ecnicas se-
guem duas avenidas principais, que, embora levem ao mesmo objetivo, atravessam ca-
minhos muito diferentes. As t´ecnicas mais conhecidas, como o trabalho de Stephenson
et al. [Stephenson et al. 2000] ou Mahlke [Mahlke et al. 2001], baseiam-se em algorit-
mos iterativos. Em outras palavras, esses m´etodos interpretam as instruc¸˜oes de um pro-
grama abstratamente. O programa ´e interpretado de forma que o valor abstrato, isso
´e, o intervalo, associado a cada vari´avel inteira somente cresce. Um operador espe-
cial, conhecido como alargamento, assegura que esse crescimento termina ap´os algumas
iterac¸˜oes. Existem implementac¸˜oes desses algoritmos em compiladores industriais, como
Open64 ou LAO, usado pela companhia STMicroelectronics. A maior parte dos arti-
gos acadˆemicos, contudo, descrevem algoritmos que resolvem a an´alise de largura de
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
220 c 2014 SBC — Soc. Bras. de Computação
intervalos de forma n˜ao iterativa. Entre esses trabalhos, citam-se as t´ecnicas de Su e Wag-
ner [Su and Wagner 2005], Gawlitza et al. e Rodrigues et al.. Exceto o algoritmo de
Rodrigues et al., presente em LLVM, n˜ao sabemos de outras implementac¸˜oes de algorit-
mos n˜ao iterativos em compiladores de uso industrial.
Em que nosso trabalho difere das t´ecnicas j´a existentes. O foco deste artigo n˜ao ´e em
algoritmos de largura de vari´aveis per se. N´os queremos aplicar tais t´ecnicas em sistemas
distribu´ıdos. Com tal prop´osito, podemos utilizar qualquer algoritmo existente. Neste
trabalho usamos o m´etodo de Rodrigues et al.. Nossa escolha foi motivada por raz˜oes
puramente pragm´aticas: esse m´etodo j´a estava implementado sobre o compilador LLVM,
o qual usamos em nossos experimentos. Nessa flexibilidade, conforme j´a mencionamos
antes, reside muito da beleza de nossa abordagem: as t´ecnicas descritas neste artigo, como
a propagac¸˜ao de valores entre n´os de programas distribu´ıdos, a inferˆencia de formatos de
mensagens e a associac¸˜ao de valores abstratos a campos de mensagens s˜ao ortogonais `a
t´ecnica de largura de vari´aveis usada.
An´alise de sistemas distribu´ıdos. Teixeira et al. introduziram o algoritmo que usa-
mos para encontrar canais impl´ıcitos em sistemas distribu´ıdos. Aquele trabalho identi-
fica canais de comunicac¸˜ao com base nos comandos de rede. A partir desses coman-
dos, Teixeira et al. realizam a interconex˜ao dos grafos de controle de fluxo de cada
programa. Existem outros trabalhos desenvolvidos com prop´osito semelhante. Entre
eles, destacamos Kleenet, de Sasnauskas et al. [Sasnauskas et al. 2010] e T-Check, de
Li et al. [Li and Regehr 2010]. Essas ferramentas executam o sistema simbolicamente
a procura de defeitos de software e permitem a explorac¸˜ao autom´atica de caminhos de
execuc¸˜ao em aplicac¸˜oes distribu´ıdas. Se uma asserc¸˜ao falha, essas ferramentas registram
o caso de teste para que o cen´ario possa ser repetido.
Em que nosso trabalho difere das t´ecnicas j´a existentes. O trabalho de Teixeira et al.
prop˜oe um arcabouc¸o para a an´alise de sistemas distribu´ıdos, mas n˜ao implementa qual-
quer an´alise sobre ele. Os autores daquele projeto n˜ao tiveram, por exemplo, de lidar com
o layout de mensagens. Tampouco foi essa uma preocupac¸˜ao de Sasnauskas et al. e Li
et al.. Nesses dois casos, o desenvolvedor deve marcar vari´aveis para serem simb´olicas e
deve escrever asserc¸˜oes sobre o estado do sistema, ou seja, usu´ario deve, explicitamente,
indicar ao analisador est´atico como dados trafegam em mensagens. Esse passo ´e manual
e requer conhecimento sobre a l´ogica da aplicac¸˜ao e estruturas de dados. Assim, a com-
plexidade da soluc¸˜ao depende das entradas simb´olicas e do n´umero de n´os. Os autores de
Kleenet, por exemplo, reportaram que mesmo com entradas simb´olicas pequenas e pou-
cos n´os, algumas aplicac¸˜oes geram milhares de caminhos de execuc¸˜ao. Nossa abordagem
´e mais autom´atica: o desenvolvedor indica quais func¸˜oes fazem a comunicac¸˜ao de rede
(passo tamb´em necess´ario para os trabalhos relacionados) e o compilador descobre como
os dados s˜ao passados, sem qualquer intervenc¸˜ao do usu´ario.
6. Conclus˜ao
Este artigo descreveu uma forma de inferir a largura de vari´aveis em programas dis-
tribu´ıdos. Essa t´ecnica d´a a uma ferramenta de an´alise de c´odigo mais subs´ıdios para en-
contrar vulnerabilidades em aplicac¸˜oes distribu´ıdas. Demonstramos esse fato mostrando
como nossa an´alise nos permite proteger c´odigo contra vulnerabilidades devido a estouro
de operac¸˜oes aritm´eticas em valores inteiros. Nossa t´ecnica exige m´ınima intervenc¸˜ao do
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
221 c 2014 SBC — Soc. Bras. de Computação
usu´ario, a saber, a indicac¸˜ao de quais func¸˜oes fazem acesso `a rede. Como trabalho futuro,
pretendemos usar o arcabouc¸o descrito neste artigo para sanear programas contra ataques
de estouro de buffer. Estamos j´a trabalhando ativamente para alcanc¸ar tal objetivo.
Agradecimentos Este projeto ´e financiado pela Intel, pelo CNPq e pela FAPEMIG.
Referˆencias
Brumley, D., Song, D. X., cker Chiueh, T., Johnson, R., and Lin, H. (2007). RICH:
Automatically protecting against integer-based vulnerabil ities. In NDSS. USENIX.
Cousot, P. and Cousot, R. (1977). Abstract interpretation: a unified lattice model for static
analysis of programs by construction or approximation of fixpoints. In POPL, pages
238–252. ACM.
Dietz, W., Li, P., Regehr, J., and Adve, V. (2012). Understanding integer overflow in
c/c++. In ICSE, pages 760–770. IEEE.
Dowson, M. (1997). The ariane 5 software failure. SIGSOFT, 22(2):84–.
Gawlitza, T., Leroux, J., Reineke, J., Seidl, H., Sutre, G., and Wilhelm, R. (2009). Poly-
nomial precise interval analysis revisited. Efficient Algorithms, 1:422 – 437.
Lattner, C. and Adve, V. S. (2004). LLVM: A compilation framework for lifelong program
analysis & transformation. In CGO, pages 75–88. IEEE.
Li, P. and Regehr, J. (2010). T-check: Bug finding for sensor networks. In IPSN, pages
174–185.
Logozzo, F. and Fahndrich, M. (2008). Pentagons: a weakly relational abstract domain
for the efficient validation of array accesses. In SAC, pages 184–188. ACM.
Mahlke, S., Ravindran, R., Schlansker, M., Schreiber, R., and Sherwood, T. (2001).
Bitwidth cognizant architecture synthesis of custom hardware accelerators. TCADICS,
20(11):1355–1371.
Oh, H., Brutschy, L., and Yi, K. (2011). Access analysis-based tight localization of abs-
tract memories. In VMCAI, pages 356–370. Springer.
Rodrigues, R. E., Campos, V. H. S., and Pereira, F. M. Q. (2013). A fast and low overhead
technique to secure programs against integer overflows. In CGO. ACM.
Sasnauskas, R., Landsiedel, O., Alizai, M. H., Weise, C., Kowalewski, S., and Wehrle,
K. (2010). Kleenet: discovering insidious interaction bugs in wireless sensor networks
before deployment. In IPSN, pages 186–196. ACM.
Stephenson, M., Babb, J., and Amarasinghe, S. (2000). Bitwidth analysis with application
to silicon compilation. In PLDI, pages 108–120. ACM.
Su, Z. and Wagner, D. (2005). A class of polynomially solvable range constraints for
interval analysis without widenings. Theoretical Computer Science, 345(1):122–138.
Teixeira, F., Pereira, F., Viera, G., Marcondes, P., Wong, H. C., and Nogueira, J. M.
(2014). Siot: defendendo a internet das coisas contra exploits. In SBRC, pages 85–96.
SBC.
XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014
222 c 2014 SBC — Soc. Bras. de Computação

Mais conteúdo relacionado

Destaque

Proyecto 2
Proyecto 2Proyecto 2
Proyecto 2
homero simson
 
farmosa presentazione 1.3
farmosa presentazione 1.3farmosa presentazione 1.3
farmosa presentazione 1.3Noemi De Santis
 
Venatio y Atlantico Asovac GVHSC 2015
Venatio y Atlantico Asovac GVHSC 2015 Venatio y Atlantico Asovac GVHSC 2015
Venatio y Atlantico Asovac GVHSC 2015
José Alvarez Cornett
 
Doñihue 2016
Doñihue 2016Doñihue 2016
Doñihue 2016
Hernan Cuevas
 
Godparents godchildren p5 a 5b 2015 bo
Godparents godchildren p5 a 5b 2015 boGodparents godchildren p5 a 5b 2015 bo
Godparents godchildren p5 a 5b 2015 bo
Escola
 
Hướng dẫn lập trình web với PHP - Ngày 1
Hướng dẫn lập trình web với PHP - Ngày 1Hướng dẫn lập trình web với PHP - Ngày 1
Hướng dẫn lập trình web với PHP - Ngày 1
Nguyễn Tuấn Quỳnh
 
Short case...Spinal dysraphism
Short case...Spinal dysraphismShort case...Spinal dysraphism
Short case...Spinal dysraphism
Professor Yasser Metwally
 
direct ophthalmoscope
direct ophthalmoscopedirect ophthalmoscope
direct ophthalmoscope
Mohammad Khabbaz
 
Infografias poderes publicos en Venezuela
Infografias poderes publicos en VenezuelaInfografias poderes publicos en Venezuela
Infografias poderes publicos en Venezuela
mariana veliz
 
English for business - A
English for business - AEnglish for business - A
English for business - A
Daniel Avelino
 

Destaque (11)

Proyecto 2
Proyecto 2Proyecto 2
Proyecto 2
 
farmosa presentazione 1.3
farmosa presentazione 1.3farmosa presentazione 1.3
farmosa presentazione 1.3
 
Venatio y Atlantico Asovac GVHSC 2015
Venatio y Atlantico Asovac GVHSC 2015 Venatio y Atlantico Asovac GVHSC 2015
Venatio y Atlantico Asovac GVHSC 2015
 
Doñihue 2016
Doñihue 2016Doñihue 2016
Doñihue 2016
 
Godparents godchildren p5 a 5b 2015 bo
Godparents godchildren p5 a 5b 2015 boGodparents godchildren p5 a 5b 2015 bo
Godparents godchildren p5 a 5b 2015 bo
 
Hướng dẫn lập trình web với PHP - Ngày 1
Hướng dẫn lập trình web với PHP - Ngày 1Hướng dẫn lập trình web với PHP - Ngày 1
Hướng dẫn lập trình web với PHP - Ngày 1
 
非人採訪術
非人採訪術非人採訪術
非人採訪術
 
Short case...Spinal dysraphism
Short case...Spinal dysraphismShort case...Spinal dysraphism
Short case...Spinal dysraphism
 
direct ophthalmoscope
direct ophthalmoscopedirect ophthalmoscope
direct ophthalmoscope
 
Infografias poderes publicos en Venezuela
Infografias poderes publicos en VenezuelaInfografias poderes publicos en Venezuela
Infografias poderes publicos en Venezuela
 
English for business - A
English for business - AEnglish for business - A
English for business - A
 

Semelhante a 0016

Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...
Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...
Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...
Dalton Valadares
 
Duly_seguranca em redes de computadores.pdf
Duly_seguranca em redes de computadores.pdfDuly_seguranca em redes de computadores.pdf
Duly_seguranca em redes de computadores.pdf
HelenaReis48
 
Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...
Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...
Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...
Ricardo Brasil
 
Artigo Do Cassio
Artigo Do CassioArtigo Do Cassio
Artigo Do Cassio
MaoNeto
 
Proposta lucas simon-rodrigues-magalhaes
Proposta lucas simon-rodrigues-magalhaesProposta lucas simon-rodrigues-magalhaes
Proposta lucas simon-rodrigues-magalhaes
lucassrod
 
Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...
Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...
Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...
Stanley Araújo
 
2015-ERIAC-I
2015-ERIAC-I2015-ERIAC-I
2015-ERIAC-I
Sergio Moraes
 
Artigo TCC-SI-2013
Artigo TCC-SI-2013Artigo TCC-SI-2013
Artigo TCC-SI-2013
Matheus Nani
 
Lamport
LamportLamport
Artigo Do Cassio
Artigo Do CassioArtigo Do Cassio
Artigo Do Cassio
MaoNeto
 
Seminario - Versão Final
Seminario - Versão FinalSeminario - Versão Final
Seminario - Versão Final
Rubens Matos Junior
 
Fórum respostas
Fórum  respostasFórum  respostas
Fórum respostas
Fernando A A Morais
 
Lista01
Lista01Lista01
Lista01
redesinforma
 
Artigo
ArtigoArtigo
Artigo
allanrafael
 
Artigo Rafael
Artigo RafaelArtigo Rafael
Artigo Rafael
allanrafael
 
poster
posterposter
poster
Ruan Costa
 
Algoritmos de Clusterização
Algoritmos de ClusterizaçãoAlgoritmos de Clusterização
Algoritmos de Clusterização
Gabriel Peixe
 
Modulo1 1
Modulo1 1Modulo1 1
DAMICORE - conceito e prática
DAMICORE - conceito e práticaDAMICORE - conceito e prática
DAMICORE - conceito e prática
Francisco Gerson A de Meneses
 
Redes neurais e lógica fuzzy
Redes neurais e lógica fuzzyRedes neurais e lógica fuzzy
Redes neurais e lógica fuzzy
Renato Ximenes
 

Semelhante a 0016 (20)

Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...
Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...
Um Injetor de Falhas para a Avaliação de Aplicações Distribuídas Baseadas no ...
 
Duly_seguranca em redes de computadores.pdf
Duly_seguranca em redes de computadores.pdfDuly_seguranca em redes de computadores.pdf
Duly_seguranca em redes de computadores.pdf
 
Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...
Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...
Aplicação das Redes Neuronais Artificiais do software STATISTICA 7.0: O caso ...
 
Artigo Do Cassio
Artigo Do CassioArtigo Do Cassio
Artigo Do Cassio
 
Proposta lucas simon-rodrigues-magalhaes
Proposta lucas simon-rodrigues-magalhaesProposta lucas simon-rodrigues-magalhaes
Proposta lucas simon-rodrigues-magalhaes
 
Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...
Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...
Linguagens Dinamicas vs Tradicionais / Potencialidades e riscos de EAI/ESB, S...
 
2015-ERIAC-I
2015-ERIAC-I2015-ERIAC-I
2015-ERIAC-I
 
Artigo TCC-SI-2013
Artigo TCC-SI-2013Artigo TCC-SI-2013
Artigo TCC-SI-2013
 
Lamport
LamportLamport
Lamport
 
Artigo Do Cassio
Artigo Do CassioArtigo Do Cassio
Artigo Do Cassio
 
Seminario - Versão Final
Seminario - Versão FinalSeminario - Versão Final
Seminario - Versão Final
 
Fórum respostas
Fórum  respostasFórum  respostas
Fórum respostas
 
Lista01
Lista01Lista01
Lista01
 
Artigo
ArtigoArtigo
Artigo
 
Artigo Rafael
Artigo RafaelArtigo Rafael
Artigo Rafael
 
poster
posterposter
poster
 
Algoritmos de Clusterização
Algoritmos de ClusterizaçãoAlgoritmos de Clusterização
Algoritmos de Clusterização
 
Modulo1 1
Modulo1 1Modulo1 1
Modulo1 1
 
DAMICORE - conceito e prática
DAMICORE - conceito e práticaDAMICORE - conceito e prática
DAMICORE - conceito e prática
 
Redes neurais e lógica fuzzy
Redes neurais e lógica fuzzyRedes neurais e lógica fuzzy
Redes neurais e lógica fuzzy
 

Último

Tipos de pontos e suturas técnicas de sutura
Tipos de pontos e suturas técnicas de suturaTipos de pontos e suturas técnicas de sutura
Tipos de pontos e suturas técnicas de sutura
DelcioVumbuca
 
5. SISTEMA ENDOCRINO-- (2).pptx florentino
5. SISTEMA ENDOCRINO-- (2).pptx florentino5. SISTEMA ENDOCRINO-- (2).pptx florentino
5. SISTEMA ENDOCRINO-- (2).pptx florentino
AmaroAlmeidaChimbala
 
Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...
Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...
Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...
Prof. Marcus Renato de Carvalho
 
Síndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptx
Síndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptxSíndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptx
Síndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptx
marjoguedes1
 
Vacina, conceito, tipos, produção, aplicaçãopdf
Vacina, conceito, tipos, produção, aplicaçãopdfVacina, conceito, tipos, produção, aplicaçãopdf
Vacina, conceito, tipos, produção, aplicaçãopdf
rickriordan
 
Programa de Saúde do Adolescente( PROSAD)
Programa de Saúde do Adolescente( PROSAD)Programa de Saúde do Adolescente( PROSAD)
Programa de Saúde do Adolescente( PROSAD)
sula31
 
A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)
A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)
A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)
Luiz Henrique Pimentel Novais Silva
 
Apostila Gerência de Riscos PDF voltado para Segurança do Trabalho
Apostila Gerência de Riscos PDF   voltado para Segurança do TrabalhoApostila Gerência de Riscos PDF   voltado para Segurança do Trabalho
Apostila Gerência de Riscos PDF voltado para Segurança do Trabalho
CatieleAlmeida1
 

Último (8)

Tipos de pontos e suturas técnicas de sutura
Tipos de pontos e suturas técnicas de suturaTipos de pontos e suturas técnicas de sutura
Tipos de pontos e suturas técnicas de sutura
 
5. SISTEMA ENDOCRINO-- (2).pptx florentino
5. SISTEMA ENDOCRINO-- (2).pptx florentino5. SISTEMA ENDOCRINO-- (2).pptx florentino
5. SISTEMA ENDOCRINO-- (2).pptx florentino
 
Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...
Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...
Livro do Instituto da Saúde: amplia visões e direitos no ciclo gravídico-puer...
 
Síndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptx
Síndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptxSíndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptx
Síndrome do Desconforto Respiratório do Recém-Nascido (SDR).pptx
 
Vacina, conceito, tipos, produção, aplicaçãopdf
Vacina, conceito, tipos, produção, aplicaçãopdfVacina, conceito, tipos, produção, aplicaçãopdf
Vacina, conceito, tipos, produção, aplicaçãopdf
 
Programa de Saúde do Adolescente( PROSAD)
Programa de Saúde do Adolescente( PROSAD)Programa de Saúde do Adolescente( PROSAD)
Programa de Saúde do Adolescente( PROSAD)
 
A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)
A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)
A DISSOLUÇÃO DO COMPLEXO DE ÉDIPO (1924)
 
Apostila Gerência de Riscos PDF voltado para Segurança do Trabalho
Apostila Gerência de Riscos PDF   voltado para Segurança do TrabalhoApostila Gerência de Riscos PDF   voltado para Segurança do Trabalho
Apostila Gerência de Riscos PDF voltado para Segurança do Trabalho
 

0016

  • 1. Prevenc¸˜ao de Ataques em Sistemas Distribu´ıdos via An´alise de Intervalos Vitor Mendes Paisante, Luiz Felipe Zafra Saggioro, Raphael Ernani Rodrigues, Leonardo Barbosa Oliveira, Fernando Magno Quint˜ao Pereira 1 Departamento de Ciˆencia da Computac¸˜ao – UFMG Av. Antˆonio Carlos, 6627 – 31.270-010 – Belo Horizonte – MG – Brazil {paisante,luizfzsaggioro,raphael,leob,fernando}@dcc.ufmg.br Abstract. The range analysis of integer variables determines the lowest and highest bounds that each variable assumes throughout the execution of a pro- gram. This technique is vital to detect a plethora of software vulnerabilities but the literature does not describe any principled way to apply range analy- sis on distributed systems. This negligence is unfortunate, as networks are the most common targets of software attacks. The goal of this paper is to set right this omission. Capitalizing on a recent algorithm to infer communication proto- cols, we have designed, implemented and tested a range analysis for distributed systems. Our contribution, a holistic view of the system, is more precise than analyzing each system module independently. In this paper we support this sta- tement through a number of examples, and experiments performed on top of the SPEC CPU 2006 benchmarks. A prototype of our tool, implemented on the LLVM compiler, is available for scrutiny. Resumo. A an´alise de largura de vari´aveis determina o maior e menor valores que cada vari´avel inteira de um programa pode assumir durante a sua execuc¸˜ao. Tal t´ecnica ´e de suma importˆancia para detectar vulnerabilidades em programas mas, at´e o momento, n˜ao existe abordagem que aplique essa an´alise em siste- mas distribu´ıdos. Tal omiss˜ao ´e s´eria, uma vez que esse tipo de sistema ´e alvo comum de ataques de software. O objetivo deste artigo ´e preencher tal lacuna. Valendo-nos de um algoritmo recente para inferir protocolos de comunicac¸˜ao, n´os projetamos, implementamos e testamos uma an´alise de largura de vari´aveis para sistemas distribu´ıdos. Nosso algoritmo, ao prover uma vis˜ao hol´ıstica do sistema distribu´ıdo, ´e mais preciso que analisar cada parte daquele sistema se- paradamente. Demonstramos tal fato via uma s´erie de exemplos e experimentos realizados sobre os programas presentes em SPEC CPU 2006. Um prot´otipo de nossa ferramenta, implementado sobre o compilador LLVM, est´a dispon´ıvel para escrut´ınio. 1. Introduc¸˜ao A an´alise de largura de intervalos [Cousot and Cousot 1977], ´e uma das t´ecnicas mais importantes que compiladores usam para encontrar vulnerabilidades em programas. Essa an´alise determina, para cada vari´avel inteira usada em um programa, quais s˜ao o menor e o maior valores que ela pode assumir. Tal informac¸˜ao permite ao compilador detectar a possibilidade de ocorrˆencia de dois fenˆomenos que comprometem a seguranc¸a de progra- mas. O primeiro deles ´e o acesso fora de limites de arranjos. Esse evento ocorre quando XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 209 c 2014 SBC — Soc. Bras. de Computação
  • 2. uma vari´avel inteira i indexa um enderec¸o inv´alido a partir de um ponteiro base a. Erros assim s˜ao comuns em linguagens fracamente tipadas, como C, uma vez que a express˜ao a[i] n˜ao assegura que i seja menor que o maior enderec¸o dereferenci´avel a partir de a. O segundo fenˆomeno que a an´alise de largura de vari´aveis descobre estaticamente s˜ao os estouros de inteiros. Uma operac¸˜ao como j = i+1, em linguagens como C, C++ ou Java, pode retornar um valor j menor que i, se i for o maior inteiro represent´avel. Por raz˜oes que discutiremos na sec¸˜ao 4, essa semˆantica pode levar a vulnerabilidades de software. A an´alise de largura de vari´aveis existe h´a quase 40 anos. Desde a sua concepc¸˜ao original, em 1977 [Cousot and Cousot 1977], v´arios desafios re- lacionados `a implementac¸˜ao dessa an´alise foram superados, tanto em termos de precis˜ao [Gawlitza et al. 2009, Su and Wagner 2005], quanto em termos de eficiˆencia [Logozzo and Fahndrich 2008]. Recentemente, por exemplo, cientistas de- monstraram como propagar informac¸˜oes de largura de vari´aveis em estruturas de da- dos [Oh et al. 2011], eliminando um dos ´ultimos entraves ao projeto de an´alises de grande precis˜ao. Entretanto, pesquisadores ainda n˜ao haviam abordado a an´alise de largura de vari´aveis em programas distribu´ıdos. O presente artigo trata desta abordagem. O grande empecilho `a an´alise de sistemas distribu´ıdos devia-se a um fato sim- ples: at´e pouco tempo atr´as n˜ao havia m´etodo confi´avel para determinar, estaticamente, quais operac¸˜oes de envio e recepc¸˜ao de mensagens se comunicam. Em outras palavras, uma vez que operac¸˜oes como receive, que coleta mensagens da rede, eram conside- radas inseguras, pouco se podia assumir quanto aos valores coletados, j´a visto que eles podem provir de quaisquer fontes. Entretanto, esse problema foi superado por Teixeira et al. [Teixeira et al. 2014] neste ano de 2014. Teixeira et al. desenvolveram um algoritmo que infere canais de comunicac¸˜ao entre programas que integram um sistema distribu´ıdo. Valendo-nos de tal m´etodo, n´os projetamos, implementamos e testamos um algoritmo que propaga informac¸˜oes de largura de vari´aveis entre n´os que se comunicam em uma rede. Nossa soluc¸˜ao para o problema da an´alise de largura de vari´aveis em sistemas distribu´ıdos consiste em cinco passos. (i) N´os determinamos quais dados representam as mensagens que um programa manipula. (ii) N´os aplicamos a an´alise de largura de vari´aveis nesses dados, para determinar o layout das mensagens do programa. (iii) Usando as t´ecnicas de Teixeira et al., n´os determinamos quais os canais de comunicac¸˜ao existem entre os programas distribu´ıdos. (iv) N´os emparelhamos as mensagens trocadas por esse canal, para determinar quais dados est˜ao fluindo de um programa para o outro. (v) N´os executamos a an´alise de largura de vari´aveis uma segunda vez, agora sobre todo o sistema distribu´ıdo, obtendo resultados finais. Dentre esses cinco passos, somente (iii) n˜ao ´e uma contribuic¸˜ao original deste artigo. Enfatizamos que os passos (i) e (ii) descobrem n˜ao somente o layout de mensagens, mas o layout de arranjos em geral. O fato de podermos entender, de forma autom´atica, como campos est˜ao dispostos em mensagens ´e uma con- sequˆencia dessa generalidade. Nossa ferramenta foi implementada sobre o compilador LLVM [Lattner and Adve 2004], e est´a dispon´ıvel publicamente. 2. Contextualizac¸˜ao A fim de ilustrar a importˆancia do problema abordado neste artigo, usaremos os dois pro- gramas vistos na figura 1. O c´odigo mostrado na parte (a) da figura lˆe uma quantidade N de caracteres da entrada padr˜ao, e os envia atrav´es de uma conex˜ao de rede, para o programa XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 210 c 2014 SBC — Soc. Bras. de Computação
  • 3. send(1); ack = recv() if (ack == 13) { N = getc(); send(N); i = 0; while (i < N) { s = getc(); send(s); ack = recv(); if (ack != 17) { break; } else { s = getc(); i++; } } send('0'); } msg = recv(); if (msg == 1) { send(13); size = recv(); j = 0; buf = malloc(size); while (true) { c = recv(); if (msg != '0') send(17); buf[j] = c; j++; else break; } } else { send(0); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 15 16 17 18 16 17 18 19 (a) (b) Figura 1. Canais impl´ıcitos de comunicac¸ ˜ao em programa distribu´ıdo. na figura 1 (b). O programa (b) recebe esses caracteres e os armazena em um arranjo de tamanho N. Caso fosse poss´ıvel escrever mais que N caracteres no arranjo usado no pro- grama (b), ent˜ao observar´ıamos a ocorrˆencia de um fenˆomeno chamado buffer overflow. Esse ´e um dos principais meios que atacantes usam para comprometer o funcionamento de programas. O nosso programa exemplo, contudo, ´e seguro: o programa (a) nunca transmite para o programa (b) mais que N bytes de dados. Entretanto, a an´alise individual do programa (b) n˜ao nos permite inferir tal fato: na ausˆencia de maiores informac¸˜oes, um analisador est´atico deve, conservadoramente, assumir que o lac¸o na linha 7 daquele programa pode executar mais que N iterac¸˜oes. Cada operac¸˜ao de send que se comunica com uma instruc¸˜ao recv cria um ca- nal de comunicac¸˜ao impl´ıcito. A definic¸˜ao de todos os canais impl´ıcitos em um sistema distribu´ıdo ´e um problema indecid´ıvel. Existem, contudo, algoritmos que apontam a pos- sibilidade de existˆencia de tais canais de forma relativamente precisa. Um deles foi recen- temente proposto por Teixeira et al.. Os poss´ıveis canais de comunicac¸˜ao impl´ıcitos que esse algoritmo encontra aparecem indicados pelas setas tracejadas na figura 1. Analisando esses canais, pode-se concluir que as vari´aveis N e size possuem o mesmo valor. 3. Arcabouc¸o de An´alises Est´aticas Nossa an´alise distribu´ıda de largura de vari´aveis segue a sequˆencia de passos mostrada na figura 2. A an´alise de segmentac¸˜ao infere os diferentes campos que constituem cada mensagem trocada em um sistema distribu´ıdo. A an´alise local de intervalos nos per- mite encontrar qual informac¸˜ao est´a armazenada em cada campo de uma mensagem. A propagac¸˜ao de informac¸˜oes nos diz quais dados fluem de um programa para outro, efe- tivamente habilitando a ´ultima fase de nossa abordagem: a an´alise global de intervalos. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 211 c 2014 SBC — Soc. Bras. de Computação
  • 4. Análise de Segmentação (seção 3.1) Análise Local de Intervalos (seção 3.1) Inferência de Formato de Mensagens (seção 3.1) Propagação de Informação (seção 3.2) Análise Global de Intervalos (seção 3.3) Figura 2. An´alises est´aticas que comp˜oem o nosso arcabouc¸o de inferˆencia de largura de vari´aveis em sistemas distribu´ıdos. Todas essas etapas s˜ao contribuic¸ ˜oes deste artigo. Cada uma dessas an´alises ´e discutida nas pr´oximas sec¸˜oes deste artigo. 3.1. Inferˆencia Autom´atica de Formato de Mensagens O objetivo deste trabalho ´e portar uma an´alise de largura de vari´aveis tradicional para um sistema distribu´ıdo. Para alcanc¸ar esse objetivo, o primeiro desafio que precisamos vencer ´e como entender o formato das mensagens trocadas via rede. Normalmente, uma mensagem ´e implementada como um arranjo. Algumas c´elulas desses arranjos s˜ao agru- padas em campos. Diferentes programas encadeiam esses campos de diferentes maneiras. Campos de mensagens podem conter, por exemplo, seu opcode1 , o identificador do reme- tente, um contador de tempo (costumeiramente conhecido como time-stamp), e dados. A figura 3 (a) ilustra um programa que cria um dentre dois tipos diferentes de mensagens, e envia a mensagem criada atrav´es de uma operac¸˜ao send. An´alise Local de Intervalos. Para inferir como informac¸˜oes s˜ao passadas entre n´os que se comunicam via rede, utilisamos a mesma an´alise de largura de vari´aveis que queremos portar para o mundo distribu´ıdo. Por´em, dessa vez n´os executamos tal an´alise localmente, isto ´e, de forma individual para cada programa que faz parte do sistema distribu´ıdo. A an´alise de largura de vari´aveis local nos d´a, para cada vari´avel inteira, uma func¸˜ao R, definida da seguinte maneira: R(v) = [l, u], {l, u} ⊂ Z ∪ {−∞, +∞}, l ≤ u Como existem v´arias implementac¸˜oes de an´alises de intervalos descritas na li- teratura [Cousot and Cousot 1977, Gawlitza et al. 2009, Rodrigues et al. 2013, Su and Wagner 2005], n´os omitiremos os detalhes do algoritmo que infere a func¸˜ao R automaticamente. Ao leitor interessado, recomendamos o trabalho de Rodrigues et al. [Rodrigues et al. 2013], que descreve uma implementac¸˜ao eficiente de tal an´alise. Assumiremos, portanto, a existˆencia de uma t´ecnica para construir a func¸˜ao R, que nos informa, para cada vari´avel v, uma estimativa do menor e do maior valores que v assume durante a execuc¸˜ao de um programa. An´alise de Segmentac¸˜ao. De posse da func¸˜ao R, passamos `a segunda fase de nossa t´ecnica. Nessa etapa, nosso objetivo ´e inferir para cada ponteiro p uma tabela de seg- mentos que o descreva. A tabela de segmentos associada a um ponteiro p ´e uma lista de intervalos que podem ser usados para indexar partes de p. Continuando com o nosso exemplo, a figura 3 (b) mostra os intervalos que podem ser usados para indexar o ar- ranjo apontado pelo ponteiro a. Os diferentes ponteiros usados para carregar dados em 1 O opcode de uma mensagem ´e um valor que descreve o tipo daquela mensagem, e permite ao seu receptor escolher a forma de tratamento mais adequado para ela. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 212 c 2014 SBC — Soc. Bras. de Computação
  • 5. a = malloc(40); if (first_msg) { a1 = a + 0; *a1 = ACK; // ack is 1 a2 = a + 1; for (i = 0; i < 4; i++) { a3 = a2 + i; *a3 = id >> i; } a4 = a + 5; for (i = 0; i < 35; i++) { a5 = a4 + i; *a5 = getc() % 100; } a6 = a + 39; *a6 = '0'; } else { a7 = a + 0; *a7 = EOF; // eof is 7 a8 = a + 1; for (i = 0; i < 39; i++) { a9 = a8 + i; *a9 = -1; } } send(a); a1 a2 a4 a6 a3 a5 a7 a9 a8 a3 ! a9 a5 ! a9 a6 ! a9(a) (b) (c) a1 ! a7{a1, a7} {a2, a3, a8, a9} {a3, a9} {a4, a5, a9} {a5, a9} {a6, a9} (d) Figura 3. (a) Exemplo de programa que cria e envia mensagens. (b) Os intervalos dos v´arios ponteiros que podem indexar blocos na mensagem. (c) Outra vis˜ao dos intervalos de ponteiros, agrupados por ´area comum de indexac¸ ˜ao. (d) A tabela de segmentos da mensagem, formada pelos diferentes ponteiros usados para efetivamente armazenar dados nela via operac¸ ˜oes de carregamento. a = malloc(v) ⇒ T(a) = [0, u], sendo R(v) = [l, u] a′ = a + c ⇒    R(a′ ) = [min(l + c, l′ ), max(u + c, u′ )], sendo R(a) = [l, u], R(a′ ) = [l′ , u′ ] T(a) = T(a′ ) ∗a = x ⇒ join(T(a), R(a)) Figura 4. Equac¸ ˜oes usadas para encontrar os segmentos que constituem cada arranjo de um programa. um arranjo determinam a sua tabela de segmentos. Assim, a tabela de segmentos para o ponteiro a de nosso exemplo pode ser vista na figura 3 (d). A tabela de segmentos T associada a um arranjo a ´e constru´ıda de acordo com as equac¸˜oes mostradas na figura 4. A func¸˜ao merge, usada para tratar operac¸˜oes de carregamento, e.g., ∗a = x, ´e definida na figura 5. Nessa implementac¸˜ao, n´os usamos a sintaxe de ML, uma linguagem de programac¸˜ao funcional. Tabelas s˜ao representadas como listas de tu- XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 213 c 2014 SBC — Soc. Bras. de Computação
  • 6. fun assertContiguous [] = true | assertContiguous [_] = true | assertContiguous ((l1, u1, _) :: (l2, u2, p) :: r) = u1 = l2 andalso assertContiguous ((l2, u2, p) :: r) fun merge [] (l, u, p) = [(l, u, [p])] | merge ((ll, uu, pp)::r) (l, u, p) = if l > uu then (ll, uu, pp) :: merge r (l, u, p) else if l = ll then if u < uu then (ll, u, p::pp) :: (u, uu, pp) :: r else (ll, uu, p::pp) :: merge r (uu, u, p) else if u < uu then (ll, l, pp) :: (l, u, p::pp) :: (u, uu, pp) :: r else (ll, l, pp) :: (l, uu, p::pp) :: merge r (uu, u, p) fun join t (l, u, p) = if assertContiguous t then merge t (l, u, p) else nil join [(0, 39, [a])] (0, 1, a1) = [ (0,1,[a1,a]),(1,39,[a]) ] join [ (0,1,[a1,a]),(1,39,[a]) ] (1, 5, a3) = [ (0,1,[a1,a]),(1,5,[a3,a]), (5,39,[a]) ] join [ (0,1,[a1,a]),(1,5,[a3,a]),(5,39,[a]) ] (1, 5, a9) = [ (0,1,[a1,a]),(1,5,[a9,a3,a]), (5,39,[a]) ] join [ (0,1,[a1,a]),(1,5,[a9,a3,a]), (5,39,[a]) ] (5, 38, a5) = [ (0,1,[a1,a]),(1,5,[a9,a3,a]), (5,38,[a5,a]),(38,39,[a]) ] Figura 5. Algoritmo que faz o emparelhamento de tabelas de ponteiros. `A direita do algoritmo mostramos algumas chamadas da func¸ ˜ao join para os ponteiros vistos na figura 3 (a). plas. Cada tupla possui trˆes elementos, e.g., (l, u, pp). Os inteiros l e u represen- tam o in´ıcio e o final do segmento. A lista pp guarda todos os ponteiros que s˜ao usados para indexar aquele segmento. O operador ::, em ML, denota a concatenac¸˜ao de listas. Assim, a tabela vista na figura 3 (c) ´e representada pela seguinte notac¸˜ao: [(0, 1, [a1, a7]), (1, 5, [a3, a9]), (5, 38, [a5, a9]), (38, 39, [a6, a9])]. Note que os segmentos determinam uma classe de equivalˆencia sobre a tabela. Em outras palavras, cada c´elula de um arranjo pertence a um segmento e a intersec¸˜ao de dois segmentos diferentes sem- pre ´e vazia. Essas propriedades implicam em contig¨uidade, isso ´e, o intervalo final de um segmento ´e o intervalo inicial de seu vizinho. N´os salientamos essa propriedade via a func¸˜ao assertContiguous, a qual pode ser vista na figura 5. A func¸˜ao join recebe uma tabela t e um intervalo para ser inserido em t. Verificada a contig¨uidade de t, join modifica t via uma invocac¸˜ao de merge, para que ela passe a conter o novo segmento. As diversas possibilidades de modificac¸˜ao s˜ao vistas na parte direita da figura 5. Qu˜ao precisa ´e nossa an´alise de segmentac¸˜ao? Neste artigo, estamos interessados em descobrir o layout de arranjos usados como mensagens em sistemas distribu´ıdos. Por ou- tro lado, a nossa an´alise de segmentac¸˜ao ´e mais geral: ela descobre segmentos em qual- quer arranjo usado em um programa. Assim, podemos mensurar a precis˜ao da an´alise contando o n´umero de segmentos descobertos por arranjo: quanto mais segmentos desco- brirmos por arranjo, mais precisa ´e nossa an´alise de segmentac¸˜ao. A figura 6 mostra esse n´umero para os arranjos nos programas de SPEC CPU 2006. Essa tabela inclui todos os arranjos encontrados naquele benchmark. Essa abrangˆencia nos d´a uma id´eia muito me- lhor da precis˜ao de nossa an´alise, que se restring´ıssemos esse experimento somente aos arranjos trocados como mensagens em sistemas distribu´ıdos, pois esses seriam poucos. A figura 6 conta o n´umero de tabelas de segmentos, em vez do n´umero de arran- jos, pois a mesma tabela pode ser formada por arranjos diferentes. Isso acontece quanto a an´alise de ponteiros de LLVM n˜ao consegue dizer se dois ponteiros podem ou n˜ao refe- XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 214 c 2014 SBC — Soc. Bras. de Computação
  • 7. Benchmark #Tabelas #m´edio de segmentos Maior tabela %Arranjos 433.milc 631 3 34 85.00% 444.namd 617 4 44 72.00% 447.dealII 9,843 2 109 75.00% 450.soplex 2,784 2 55 89.00% 470.lbm 24 16 152 99.00% 401.bzip2 493 2 92 93.00% 429.mcf 103 3 19 68.00% 456.hmmer 4,872 2 64 86.00% 458.sjeng 356 2 14 96.00% 462.libquantum 343 2 6 99.00% 464.h264ref 15,436 2 45 97.00% 471.omnetpp 2,518 2 50 58.00% 473.astar 333 3 22 95.00% 483.xalancbmk 25,194 2 62 82.00% Total/M´edia/Max/M´edia 4,539 3.4 152 85.29% Figura 6. Precis˜ao de nossa an´alise de segmentac¸ ˜ao. renciar o mesmo enderec¸o base. Pela figura 6, vemos que, em m´edia, cada tabela possui 3.4 segmentos. A maior tabela que observamos, presente em lbm, possui 152 segmen- tos. Em outras palavras, essa tabela representa um arranjo indexado por 152 vari´aveis contendo intervalos de valores diferentes. A figura 6 cont´em uma coluna %Arranjos, que descreve a porcentagem de arranjos que pudemos analisar por benchmark. Somente anali- samos arranjos cujo ponteiro base ´e conhecido. Ou seja, precisamos encontrar, no c´odigo do programa, o ponto de criac¸˜ao do arranjo. Arranjos alocados por func¸˜oes externas2 , por exemplo, n˜ao podem ser analisados. Inferˆencia de Intervalos em Campos de Mensagens. Feita a segmentac¸˜ao da mem´oria nos programas que formam o sistema distribu´ıdo, passamos `a fase de inferˆencia de valores em mensagens. Nessa etapa, estamos interessados em encontrar quais os intervalos de valores que podem ser armazenados em cada segmento. Fazemos isso via o lac¸o abaixo: • Para cada instruc¸˜ao de carregamento ∗a = x presente no programa: – Para cada segmento s que cont´em a in T(a) fac¸a ∗ R(s) = R(s) ∪ R(x) A figura 7 mostra o resultado da inferˆencia de mensagens quando aplicada no programa visto na figura 3 (a). Inicialmente, todos os segmentos da tabela associada ao arranjo a cont´em intervalos inteiros indefinidos, indicados pela notac¸˜ao [?, ?]. Durante o processamento das instruc¸˜oes de carregamento, os valores desconhecidos s˜ao substitu´ıdos pelos valores encontrados via a an´alise local de intervalos. Se duas ou mais instruc¸˜oes, tais como ∗a = x1 e ∗a = x2, carregam valores nos mesmos segmentos, ent˜ao esse segmento recebe a uni˜ao dos intervalos R(x1) ∪ R(x2). Esse fenˆomeno pode ser visto durante o processamento da instruc¸˜ao ∗a7 = EOF, na figura 7, que expande o segmento associado `a {a5, a9}, de [0, 99] para [−1, 99]. 2 Uma func¸˜ao ´e externa se o seu c´odigo fonte n˜ao est´a dispon´ıvel para nosso compilador. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 215 c 2014 SBC — Soc. Bras. de Computação
  • 8. *a1 = ACK *a3 = id >> 1 *a5 = getc % 100 *a6 = '0' *a7 = EOF *a9 = 0 {a1,a7} {a3,a9} {a5,a9} {a6,a9} [1, 1] [0, +!][1, 1] [0, +!][1, 1] [0, 99] [0, +!][1, 1] [0, 99] [0, 0] [0, +!][1, 7] [0, 99] [0, 0] ["1, +!][1, 7] ["1, 99] ["1, 0] [?, ?] [?, ?] [?, ?] [?, ?] [?, ?] [?, ?] Figura 7. Inferˆencia de intervalos inteiros na mensagem vista na figura 3. b = recv(); b1 = b + 0; msg_type = *b1; if (msg_type == ACK) { b1 = b + 5; c = malloc(35); for (j = 0; j < 35; i++) { b2 = b1 + j; tmp = *b2; c1 = c + j; *c1 = tmp; } printf(c1); } else { printf("End of Stream"); } [1, 7] [0, +!] ["1, 99] ["1, 0] b ["1, 99] ["1, 0] c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Figura 8. A esquerda, vˆe-se um trecho de c´odigo que recebe mensagens envi- adas pelo programa da figura 3 (a). `A direita, vˆeem-se mensagens recuperadas via o emparelhamento por canais impl´ıcitos. A inferˆencia de valores de mensagens termina t˜ao logo todas as instruc¸˜oes de carregamento no programa sejam processadas. Cada instruc¸˜ao ´e visitada somente uma vez, ent˜ao a terminac¸˜ao ´e garantida. Assim, a complexidade computacional dessa an´alise ´e proporcional ao n´umero de instruc¸˜oes de carregamento no programa. 3.2. Propagac¸˜ao de Informac¸˜ao entre Programas Finda a fase local de nosso algoritmo, que envolve os trˆes passos descritos na sec¸˜ao 3.1, passamos `a fase distribu´ıda de nossa an´alise. Nessa etapa, tabelas de segmentos em pro- gramas diferentes s˜ao emparelhadas, de acordo com os canais de comunicac¸˜ao inferidos usando-se o algoritmo de Teixeira et al.. Para cada canal de comunicac¸˜ao inferido, as tabe- las enviadas s˜ao emparelhadas com as tabelas recebidas. A figura 8 ilustra esse processo. O programa visto nessa figura recebe mensagens enviadas pelas instruc¸˜oes mostradas na figura 3 (a). Uma vez que existe um canal de comunicac¸˜ao impl´ıcito entre a instruc¸˜ao send da figura 3 (a) e a instruc¸˜ao recv da figura 8, temos que as tabelas associadas aos arranjos a e b devem ser emparelhadas. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 216 c 2014 SBC — Soc. Bras. de Computação
  • 9. A partir de um canal de comunicac¸˜ao impl´ıcito formado por uma operac¸˜ao de send s e uma operac¸˜ao de recv r, podemos definir as tabelas de segmentos origem e des- tino. Chamamos de tabela origem aquela associada a qualquer arranjo passado para s e destino a tabela associada a algum arranjo recebido por r. Dados esses conceitos, o empa- relhamento de tabelas de segmentos ´e uma operac¸˜ao simples, e consiste no casamento de campos cujos ´ındices se correspondem nas tabelas origem e destino. A figura 8 mostra as tabelas de segmentos associadas aos arranjos b e c, as quais obtemos via emparelhamento com a tabela origem associada ao arranjo a da figura 3 (a). 3.3. An´alise Global de Largura de Vari´aveis Terminado o emparelhamento, comec¸amos a ´ultima fase da abordagem que esse artigo prop˜oe: a an´alise global de intervalos. Essa etapa n˜ao requer qualquer algoritmo especi- almente adaptado para o universo dos sistemas distribu´ıdos. Para encontrar os intervalos associados `as vari´aveis inteiras de cada programa que integra o sistema, podemos usar qualquer algoritmo j´a descrito na literatura. As informac¸˜oes necess´arias ao correto funci- onamento do algoritmo j´a foram inferidas nos passos anteriores. Essa flexibilidade ´e uma das vantagens de nossa abordagem. Continuando com o nosso exemplo, n´os temos que a vari´avel tmp, inicializada na linha 9 da figura 8, pode conter somente valores dentro do intervalo [−1, 99]. Esse intervalo foi inferido para segmentos apontados pelo ponteiro b2 no passo de propagac¸˜ao de informac¸˜ao entre n´os comunicantes. Caso analis´assemos o programa da figura 8 em separado, ter´ıamos de assumir que a vari´avel tmp pudesse ser inicializada com qualquer valor inteiro. Essa perda de precis˜ao deve-se ao fato de uma an´alise individual n˜ao nos dar qualquer informac¸˜ao sobre dados recebidos via operac¸˜oes de recv. 4. Estudo de Caso N´os implementamos nossa an´alise sobre o compilador LLVM, vers˜ao 3.3, pois tanto o trabalho de Teixeira et al. quanto a an´alise de largura de vari´aveis de Rodrigues et al. foram constru´ıdas nesse compilador. A fim de demonstrar o funcionamento da t´ecnica proposta neste artigo, esta sec¸˜ao descreve sua utilizac¸˜ao sobre um par cliente-servidor real. O c´odigo presente aqui pode ser compilado e testado diretamente3 . O cliente usado neste estudo de caso envia para um servidor uma quantidade indeterminada de pares for- mados por nomes de funcion´arios e horas trabalhadas. As mensagens que carregam essas informac¸˜oes possuem dois campos. O servidor, ao receber cada um desses pares, mul- tiplica a quantidade de horas trabalhadas por um valor de sal´ario-base e envia de volta para o cliente uma tripla, formada pelo nome do funcion´ario, suas horas trabalhadas e seu sal´ario. A figura 9 mostra o c´odigo de nossa aplicac¸˜ao cliente, e a figura 10 mostra o c´odigo de nossa aplicac¸˜ao servidora. Por simplicidade, neste exemplo operaremos so- mente a n´ıvel de bytes. Assim, nomes s˜ao cadeias de bytes, horas trabalhadas s˜ao um byte e o sal´ario-base ´e um byte tamb´em. A figura 9 mostra, al´em do programa cliente, o protocolo de comunicac¸˜ao de nosso estudo de caso. O cliente inicialmente informa ao servidor a quantidade de 3 Devido a restric¸˜oes de espac¸o, n˜ao mostramos as diretivas #include em nosso c´odigo. Assim, os seguintes arquivos devem ser inclu´ıdos em cada programa: stdio.h, string.h, sys/socket.h e arpa/inet.h. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 217 c 2014 SBC — Soc. Bras. de Computação
  • 10. int main() { int i, num, salario_calc, descriptor, ack = 0; char buffer[42], *message, server_reply[2000]; struct sockaddr_in server; descriptor = socket(AF_INET , SOCK_STREAM , 0); server.sin_addr.s_addr = inet_addr("localhost"); server.sin_family = AF_INET; server.sin_port = htons(2000); connect(descriptor, (struct sockaddr *)&server , sizeof(server)); // Informa ao servidor quantas mensagens serao enviadas: fscanf(stdin, "%d", &num); buffer[0] = num < 100 ? num : 0; send(descriptor, buffer, 1, 0); recv(descriptor, &ack, sizeof(int), 0); if (ack) { // Envia os pares: (nome do trabalhador x horas trabalhadas): for (i = 0; i < num; i++) { // Leia o nome do trabalhador: fscanf(stdin, "%s", buffer); // Leia o numero de horas trabalhadas: fscanf(stdin, "%d", &buffer[40]); // Permite-se no maximo dez horas-extras trabalhadas: buffer[40] = buffer[40] > 10 ? 10 : buffer[40]; // Envia a mensagem: send(descriptor, buffer, strlen(buffer) * sizeof(char), 0); // Recebe o valor a ser pago ao trabalhador: recv(descriptor, buffer, strlen(buffer) * sizeof(char), 0); salario_calc = (int)buffer[41]; printf("Valor a ser pago = %dn", salario_calc); } } return 0; } Cliente Servidor [num] [1] ["nome"*, horas] ["nome"*, horas, sal] ["nome"*, horas] ["nome"*, horas, sal] . . . 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Figura 9. (Esquerda) Programa cliente. (Direita) Protocolo de comunicac¸ ˜ao. int main() { int num, i, salario, descriptor, connection, clilen; char buffer[42]; struct sockaddr_in server, client; descriptor = socket(AF_INET , SOCK_STREAM , 0); server.sin_addr.s_addr = inet_addr("localhost"); server.sin_family = AF_INET; server.sin_port = htons(2000); bind(descriptor, (struct sockaddr *)&server, sizeof(server)); connection = accept(descriptor, (struct sockaddr *)&client, &clilen); // Recebe o numero de mensagens que vao chegar: recv(connection, &num, sizeof(int), 0); // Envia um ack para o cliente: buffer[0] = 1; send(connection, buffer, 1, 0); // Leia todas as mensagens: for (i = 0; i < num; i++) { recv(connection, &buffer, strlen(buffer)*sizeof(char), 0); salario = HORA * buffer[40]; // HORA == 12 buffer[41] = salario; // Envia o salario final para o cliente: send(connection, buffer, strlen(buffer)*sizeof(char), 0); } return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [!", +"][0, 99] 411 [!", +"][1, 1] 411 [!", +"] 40 [!", +"][!", 10] 1 1 [!", +"] 40 [!", 120][!", 10] 1 126 Figura 10. Programa servidor que interage com o cliente visto na figura 9. Os segmentos de mensagens que nossa an´alise infere automaticamente s˜ao mos- trados `a direita do programa. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 218 c 2014 SBC — Soc. Bras. de Computação
  • 11. mensagens trocadas. Essa primeira mensagem ´e seguida de uma confirmac¸˜ao de re- cebimento, por parte do servidor. A partir desse ponto, o cliente passa a enviar os pares (nomes × horas) para o servidor, que lhe envia de volta as triplas (nomes × horas × sal´ario). Esse protocolo possui quatro canais impl´ıcitos de comunicac¸˜ao4 : (i) cliente : send : 15 → servidor : recv : 13, (ii) servidor : send : 16 → cliente : recv : 16, (iii) cliente : send : 27 → servidor : recv : 19 e (iv) servidor : send : 23 → cliente : recv : 29. O algoritmo proposto por Teixeira et al. descobre esses quatro canais e somente eles. Note que nesse caso, o algoritmo n˜ao reporta falsos-positivos: todos os canais inferidos s˜ao, de fato, canais v´alidos. O algoritmo proposto na sec¸˜ao 3.1 descobre os formatos de mensagens vistos `a direita da figura 10. Nesse exemplo, nosso algoritmo foi capaz de inferir, de forma pre- cisa, os intervalos de valores associados a cinco dos dez campos de mensagens trocadas entre cliente e servidor. Por exemplo, o programa cliente possui um teste na linha 25 (fi- gura 9) que garante que trabalhadores n˜ao podem fazer mais que 10 horas extra. Esse teste permite-nos determinar que somente valores no intervalo [−∞, 10] podem ser transferidos na posic¸˜ao 40 das mensagens. Valores imprecisos, associados aos intervalos [−∞, +∞], devem-se `a pouca informac¸˜ao dispon´ıvel no c´odigo fonte do programa. S˜ao imprecisos, por exemplo, os valores dos bytes associados a nomes de funcion´arios. Esses bytes podem cobrir qualquer intervalo entre [−128, 127], exatamente o dom´ınio do tipo char. 4.1. Como utilizar nossa an´alise para aumentar a seguranc¸a e a eficiˆencia de programas distribu´ıdos. Estamos, atualmente, usando os resultados de nossa an´alise para eliminar guardas sobre operac¸˜oes que podem causar estouro em aritm´etica de inteiros. Linguagens como C, C++ ou Java tratam operac¸˜oes inteiras segundo uma semˆantica modular. Se o resultado de uma instruc¸˜ao inteira for maior que o tamanho do registrador onde esse resultado ser´a armazenado, ent˜ao os bits mais significativos desse valor s˜ao descartados. Por exemplo, 6char × 22char = −124char . A literatura cont´em v´arias descric¸˜oes de ataques baseados nesse semˆantica [Brumley et al. 2007, Dietz et al. 2012]. Um ataque baseado em estouro de arranjos ´e poss´ıvel em nosso estudo de caso. Considere, por exemplo, que um usu´ario malicioso informe um valor negativo de horas na linha 23 do programa cliente (vari´avel buffer[40] na figura 9). Suponhamos que tal valor seja o inteiro negativo −75. Temos ent˜ao que o teste na linha 25 do programa cliente ´e falso. Consequentemente −75 ser´a transmitido para o servidor. No c´odigo do servidor (figura 10), a multiplicac¸˜ao na linha 20, e.g., −75char ×12char = 124char , produz um valor maior que o m´aximo n´umero de horas cuja intenc¸˜ao do desenvolvedor seria permitir, isto ´e, 10char × 12char = 120char . Esse tipo de falha de seguranc¸a ´e dif´ıcil de ser detectado sem o aux´ılio de ferramentas de an´alise est´atica, e pode levar a situac¸˜oes catastr´oficas. A t´ıtulo de exemplo, em 1996, o foguete Ariane 5 foi perdido devido a um estouro de inteiros – tal erro de software custou ao programa espacial europeu cerca de US$ 370 milh˜oes [Dowson 1997]. Existem diversas t´ecnicas para sanear programas contra estouros de operac¸˜oes inteiras. Recentemente, por exemplo, Rodrigues et al. propuseram um gerador de c´odigo que instrumenta operac¸˜oes inteiras em um programa. Essa instrumentac¸˜ao invoca c´odigo 4 N´umeros ao lado do nome do programa denotam linhas nas figuras 9 e figuras 10. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 219 c 2014 SBC — Soc. Bras. de Computação
  • 12. salario = HORA * buffer[40]; int tmp0 = (int) HORA; int tmp1 = (int) buffer[40]; if (tmp0 * tmp1 != HORA * buffer[40]) handleOverflow("Linha 19 - char", HORA, buffer[40]); Figura 11. Exemplo de c´odigo para verificar se houve estouro de inteiro de tratamento de erros sempre que um estouro ´e detectado. Tal t´ecnica, quando aplicada ao programa da figura 10, insere guardas na soma da linha 18, e na multiplicac¸˜ao da linha 20. Cada uma dessas guardas ´e implementada como uma combinac¸˜ao de testes condicionais e instruc¸˜oes de desvio, conforme mostrado na figura 11. Essas guardas tornam o programa instrumentado mais lento que o programa ori- ginal. Conforme reportado por Dietz et al., essa lentid˜ao pode comprometer at´e 15% do tempo de execuc¸˜ao do programa modificado [Dietz et al. 2012]. Nossa t´ecnica nos permite eliminar alguns desses testes, e tamb´em indicar ao desenvolver quais testes preci- sam ser mantidos. Por exemplo, considerando-se o programa servidor, visto na figura 10, nossa an´alise elimina o teste sobre o incremento realizado na linha 18, pois a vari´avel i ´e limitada por num, cujo intervalo superior pode ser no m´aximo 99. Por outro lado, n˜ao podemos eliminar a guarda da multiplicac¸˜ao da linha 20, pois buffer[40], caso fosse um n´umero negativo muito pequeno, causaria um estouro aritm´etico. Nossa an´alise detecta tamb´em essa possibilidade e mant´em a instrumentac¸˜ao na linha 20. 5. Trabalhos Relacionados O presente trabalho relaciona-se a pesquisa desenvolvida tanto em an´alise de c´odigo, quanto em sistemas distribu´ıdos. No primeiro caso, nossa inspirac¸˜ao mais importante deve-se a Cousot e Cousot [Cousot and Cousot 1977], que introduziram o conceito de an´alise de largura de vari´aveis. No segundo caso, contudo, nossa inspirac¸˜ao ´e bem mais recente: muito do que discutimos neste artigo foi poss´ıvel somente devido ao arcabouc¸o constru´ıdo por Teixeira et al.. No restante dessa sec¸˜ao discutiremos como nosso trabalho se relaciona com outras pesquisas nesses dois campos. An´alise de largura de vari´aveis. A an´alise de largura de vari´aveis ´e um dos exem- plos cl´assicos de interpretac¸˜ao abstrata. A t´ecnica de interpretac¸˜ao abstrata, introdu- zida por Cousot e Cousot, ´e um arcabouc¸o te´orico que permite a compiladores obter informac¸˜oes de um programa, garantindo que os algoritmos usados terminam. Existem muitas formas de se implementar an´alise de largura de intervalos [Gawlitza et al. 2009, Mahlke et al. 2001, Stephenson et al. 2000, Su and Wagner 2005]. Essas t´ecnicas se- guem duas avenidas principais, que, embora levem ao mesmo objetivo, atravessam ca- minhos muito diferentes. As t´ecnicas mais conhecidas, como o trabalho de Stephenson et al. [Stephenson et al. 2000] ou Mahlke [Mahlke et al. 2001], baseiam-se em algorit- mos iterativos. Em outras palavras, esses m´etodos interpretam as instruc¸˜oes de um pro- grama abstratamente. O programa ´e interpretado de forma que o valor abstrato, isso ´e, o intervalo, associado a cada vari´avel inteira somente cresce. Um operador espe- cial, conhecido como alargamento, assegura que esse crescimento termina ap´os algumas iterac¸˜oes. Existem implementac¸˜oes desses algoritmos em compiladores industriais, como Open64 ou LAO, usado pela companhia STMicroelectronics. A maior parte dos arti- gos acadˆemicos, contudo, descrevem algoritmos que resolvem a an´alise de largura de XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 220 c 2014 SBC — Soc. Bras. de Computação
  • 13. intervalos de forma n˜ao iterativa. Entre esses trabalhos, citam-se as t´ecnicas de Su e Wag- ner [Su and Wagner 2005], Gawlitza et al. e Rodrigues et al.. Exceto o algoritmo de Rodrigues et al., presente em LLVM, n˜ao sabemos de outras implementac¸˜oes de algorit- mos n˜ao iterativos em compiladores de uso industrial. Em que nosso trabalho difere das t´ecnicas j´a existentes. O foco deste artigo n˜ao ´e em algoritmos de largura de vari´aveis per se. N´os queremos aplicar tais t´ecnicas em sistemas distribu´ıdos. Com tal prop´osito, podemos utilizar qualquer algoritmo existente. Neste trabalho usamos o m´etodo de Rodrigues et al.. Nossa escolha foi motivada por raz˜oes puramente pragm´aticas: esse m´etodo j´a estava implementado sobre o compilador LLVM, o qual usamos em nossos experimentos. Nessa flexibilidade, conforme j´a mencionamos antes, reside muito da beleza de nossa abordagem: as t´ecnicas descritas neste artigo, como a propagac¸˜ao de valores entre n´os de programas distribu´ıdos, a inferˆencia de formatos de mensagens e a associac¸˜ao de valores abstratos a campos de mensagens s˜ao ortogonais `a t´ecnica de largura de vari´aveis usada. An´alise de sistemas distribu´ıdos. Teixeira et al. introduziram o algoritmo que usa- mos para encontrar canais impl´ıcitos em sistemas distribu´ıdos. Aquele trabalho identi- fica canais de comunicac¸˜ao com base nos comandos de rede. A partir desses coman- dos, Teixeira et al. realizam a interconex˜ao dos grafos de controle de fluxo de cada programa. Existem outros trabalhos desenvolvidos com prop´osito semelhante. Entre eles, destacamos Kleenet, de Sasnauskas et al. [Sasnauskas et al. 2010] e T-Check, de Li et al. [Li and Regehr 2010]. Essas ferramentas executam o sistema simbolicamente a procura de defeitos de software e permitem a explorac¸˜ao autom´atica de caminhos de execuc¸˜ao em aplicac¸˜oes distribu´ıdas. Se uma asserc¸˜ao falha, essas ferramentas registram o caso de teste para que o cen´ario possa ser repetido. Em que nosso trabalho difere das t´ecnicas j´a existentes. O trabalho de Teixeira et al. prop˜oe um arcabouc¸o para a an´alise de sistemas distribu´ıdos, mas n˜ao implementa qual- quer an´alise sobre ele. Os autores daquele projeto n˜ao tiveram, por exemplo, de lidar com o layout de mensagens. Tampouco foi essa uma preocupac¸˜ao de Sasnauskas et al. e Li et al.. Nesses dois casos, o desenvolvedor deve marcar vari´aveis para serem simb´olicas e deve escrever asserc¸˜oes sobre o estado do sistema, ou seja, usu´ario deve, explicitamente, indicar ao analisador est´atico como dados trafegam em mensagens. Esse passo ´e manual e requer conhecimento sobre a l´ogica da aplicac¸˜ao e estruturas de dados. Assim, a com- plexidade da soluc¸˜ao depende das entradas simb´olicas e do n´umero de n´os. Os autores de Kleenet, por exemplo, reportaram que mesmo com entradas simb´olicas pequenas e pou- cos n´os, algumas aplicac¸˜oes geram milhares de caminhos de execuc¸˜ao. Nossa abordagem ´e mais autom´atica: o desenvolvedor indica quais func¸˜oes fazem a comunicac¸˜ao de rede (passo tamb´em necess´ario para os trabalhos relacionados) e o compilador descobre como os dados s˜ao passados, sem qualquer intervenc¸˜ao do usu´ario. 6. Conclus˜ao Este artigo descreveu uma forma de inferir a largura de vari´aveis em programas dis- tribu´ıdos. Essa t´ecnica d´a a uma ferramenta de an´alise de c´odigo mais subs´ıdios para en- contrar vulnerabilidades em aplicac¸˜oes distribu´ıdas. Demonstramos esse fato mostrando como nossa an´alise nos permite proteger c´odigo contra vulnerabilidades devido a estouro de operac¸˜oes aritm´eticas em valores inteiros. Nossa t´ecnica exige m´ınima intervenc¸˜ao do XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 221 c 2014 SBC — Soc. Bras. de Computação
  • 14. usu´ario, a saber, a indicac¸˜ao de quais func¸˜oes fazem acesso `a rede. Como trabalho futuro, pretendemos usar o arcabouc¸o descrito neste artigo para sanear programas contra ataques de estouro de buffer. Estamos j´a trabalhando ativamente para alcanc¸ar tal objetivo. Agradecimentos Este projeto ´e financiado pela Intel, pelo CNPq e pela FAPEMIG. Referˆencias Brumley, D., Song, D. X., cker Chiueh, T., Johnson, R., and Lin, H. (2007). RICH: Automatically protecting against integer-based vulnerabil ities. In NDSS. USENIX. Cousot, P. and Cousot, R. (1977). Abstract interpretation: a unified lattice model for static analysis of programs by construction or approximation of fixpoints. In POPL, pages 238–252. ACM. Dietz, W., Li, P., Regehr, J., and Adve, V. (2012). Understanding integer overflow in c/c++. In ICSE, pages 760–770. IEEE. Dowson, M. (1997). The ariane 5 software failure. SIGSOFT, 22(2):84–. Gawlitza, T., Leroux, J., Reineke, J., Seidl, H., Sutre, G., and Wilhelm, R. (2009). Poly- nomial precise interval analysis revisited. Efficient Algorithms, 1:422 – 437. Lattner, C. and Adve, V. S. (2004). LLVM: A compilation framework for lifelong program analysis & transformation. In CGO, pages 75–88. IEEE. Li, P. and Regehr, J. (2010). T-check: Bug finding for sensor networks. In IPSN, pages 174–185. Logozzo, F. and Fahndrich, M. (2008). Pentagons: a weakly relational abstract domain for the efficient validation of array accesses. In SAC, pages 184–188. ACM. Mahlke, S., Ravindran, R., Schlansker, M., Schreiber, R., and Sherwood, T. (2001). Bitwidth cognizant architecture synthesis of custom hardware accelerators. TCADICS, 20(11):1355–1371. Oh, H., Brutschy, L., and Yi, K. (2011). Access analysis-based tight localization of abs- tract memories. In VMCAI, pages 356–370. Springer. Rodrigues, R. E., Campos, V. H. S., and Pereira, F. M. Q. (2013). A fast and low overhead technique to secure programs against integer overflows. In CGO. ACM. Sasnauskas, R., Landsiedel, O., Alizai, M. H., Weise, C., Kowalewski, S., and Wehrle, K. (2010). Kleenet: discovering insidious interaction bugs in wireless sensor networks before deployment. In IPSN, pages 186–196. ACM. Stephenson, M., Babb, J., and Amarasinghe, S. (2000). Bitwidth analysis with application to silicon compilation. In PLDI, pages 108–120. ACM. Su, Z. and Wagner, D. (2005). A class of polynomially solvable range constraints for interval analysis without widenings. Theoretical Computer Science, 345(1):122–138. Teixeira, F., Pereira, F., Viera, G., Marcondes, P., Wong, H. C., and Nogueira, J. M. (2014). Siot: defendendo a internet das coisas contra exploits. In SBRC, pages 85–96. SBC. XIV Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2014 222 c 2014 SBC — Soc. Bras. de Computação