Skip to content

Commit

Permalink
compile/2: add limit option
Browse files Browse the repository at this point in the history
By default, limit filter length to an arbitrary value of 8192 bytes.

Using very large filters with pcap 1.7.4 can result in a segmentation
fault:

    epcap_compile:compile(string:copies("ip and ", 50000) ++ "ip").

The most likely cause is a stack overflow. pcap_compile() in pcap 1.8.1
is thread-safe. Presumably this means memory is allocated on the heap so
as a consequence, the stack overflow may be fixed. I haven't debugged
this further yet.

The limit can be disabled by using a value of -1 as the limit option.

Some notes:

    % {ok, _}
    epcap_compile:compile(string:copies("ip or ", 50000) ++ "ip").

    % segfault
    epcap_compile:compile(string:copies("ip or ", 60000) ++ "ip").

    % {error, "out of memory"}
    epcap_compile:compile(string:copies("tcp and ", 50000) ++ "ip").

    # shell
    ulimit -s
    8192

    (seq 1 50000 | while read l; do printf "ip and "; done; echo "ip") > filter

    # works
    sudo tcpdump -d -F filter

    ulimit -s 1024
    sudo tcpdump -d -F filter
    Segmentation fault (core dumped)
  • Loading branch information
msantos committed Aug 23, 2017
1 parent 2d13c32 commit de15f3b
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 4 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ Since the library passes the filter string to pcap\_compile(3PCAP)
directly, any bugs in pcap\_compile() may cause the Erlang VM to crash. Do
not use filters from untrusted sources.

Also note very large filters may block the scheduler. For example:
Also note very large filters may block the scheduler or cause a stack
overflow. For example:

epcap_compile:compile(string:copies("ip and ", 50000) ++ "ip").

Filters larger than 8192 bytes will not be be allowed by default. See the
`limit` option to compile/2.

## REQUIREMENTS

Expand Down Expand Up @@ -55,6 +58,7 @@ These libraries are not required but can be used with epcap\_compile:
| {netmask, IPaddr}
| {dlt, integer()}
| {snaplen, integer()}
| {limit, integer()}

Filter is a string in pcap-filter(7) format.

Expand All @@ -75,6 +79,10 @@ These libraries are not required but can be used with epcap\_compile:

* a packet length of 65535 bytes

* a limit of 8192 bytes for filters. Filters larger than
this limit will return `{error, enomem}`. A limit less than
0 disables the length check.

See pcap_compile(3PCAP) for information about each of these options.


Expand Down
13 changes: 10 additions & 3 deletions src/epcap_compile.erl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%% Copyright (c) 2012-2016, Michael Santos <michael.santos@gmail.com>
%% Copyright (c) 2012-2017, Michael Santos <michael.santos@gmail.com>
%% All rights reserved.
%%
%% Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -61,7 +61,8 @@ compile(Filter) ->
{optimize, true | false} |
{netmask, non_neg_integer()} |
{dlt, integer()} |
{snaplen, integer()}
{snaplen, integer()} |
{limit, integer()}
].
-spec compile(Filter :: iodata(), compile_options())
-> {ok, [binary()]} | {error, string()}.
Expand All @@ -71,8 +72,14 @@ compile(Filter, Options) when is_binary(Filter); is_list(Filter) ->
?PCAP_NETMASK_UNKNOWN)),
Linktype = proplists:get_value(dlt, Options, ?DLT_EN10MB),
Snaplen = proplists:get_value(snaplen, Options, 16#ffff),
Limit = proplists:get_value(limit, Options, 8192),

pcap_compile(Filter, Optimize, Netmask, Linktype, Snaplen).
case iolist_size(Filter) < Limit orelse Limit < 0 of
true ->
pcap_compile(Filter, Optimize, Netmask, Linktype, Snaplen);
false ->
{error, enomem}
end.

bool(true) -> 1;
bool(false) -> 0.
Expand Down

0 comments on commit de15f3b

Please sign in to comment.