mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00
fuzzing/sipf-fmt: Add SIP format class and Flex lexer
Change-Id: Icecdfddd200932eb7f4f5fdd7e6a406472f85c18
This commit is contained in:

committed by
Adrien Béraud

parent
c79a38c6e5
commit
da7e54e45e
@ -1,11 +1,15 @@
|
||||
include $(top_srcdir)/globals.mk
|
||||
|
||||
if ENABLE_FUZZING
|
||||
|
||||
%.cpp: %.ll
|
||||
flex --outfile $@ $^
|
||||
|
||||
AM_CXXFLAGS += -I$(top_srcdir)/src -I. -include common.h
|
||||
AM_LDFLAGS += $(top_builddir)/src/libring.la
|
||||
check_PROGRAMS =
|
||||
|
||||
lib_LTLIBRARIES = libfuzz.la
|
||||
|
||||
libfuzz_la_SOURCES = lib/utils.cpp lib/supervisor.cpp lib/gnutls.cpp lib/rand.cpp lib/syslog.cpp
|
||||
libfuzz_la_SOURCES = lib/utils.cpp lib/supervisor.cpp lib/gnutls.cpp lib/rand.cpp lib/syslog.cpp lib/sip-fmt.cpp lib/sip-parser.cpp
|
||||
endif
|
||||
|
152
test/fuzzing/lib/sip-fmt.cpp
Normal file
152
test/fuzzing/lib/sip-fmt.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "lib/sip-fmt.h"
|
||||
|
||||
SIPFmt::SIPFmt(const std::vector<uint8_t>& data)
|
||||
: isValid_(false)
|
||||
|
||||
{
|
||||
parse(data);
|
||||
}
|
||||
|
||||
void
|
||||
SIPFmt::pushBody(char *bytes, size_t len)
|
||||
{
|
||||
for (size_t i=0; i<len; ++i) {
|
||||
body_.emplace_back(bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SIPFmt::setField(const std::string& field)
|
||||
{
|
||||
size_t at = field.find_first_of(':');
|
||||
|
||||
assert(at != std::string::npos);
|
||||
|
||||
setFieldValue(field.substr(0, at), field.substr(at + 1));
|
||||
}
|
||||
|
||||
void
|
||||
SIPFmt::setFieldValue(const std::string& field, const std::string& value)
|
||||
{
|
||||
std::string fieldLow;
|
||||
|
||||
fieldLow.reserve(field.size());
|
||||
|
||||
for (auto it = field.cbegin(); it != field.cend(); ++it) {
|
||||
fieldLow.push_back(tolower(*it));
|
||||
}
|
||||
|
||||
fields_[fieldLow] = value;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
SIPFmt::getField(const std::string& field) const
|
||||
{
|
||||
static std::string emptyString("");
|
||||
|
||||
std::string fieldLow;
|
||||
|
||||
fieldLow.reserve(field.size());
|
||||
|
||||
for (auto it = field.cbegin(); it != field.cend(); ++it) {
|
||||
fieldLow.push_back(tolower(*it));
|
||||
}
|
||||
|
||||
try {
|
||||
return fields_.at(fieldLow);
|
||||
} catch (...) {
|
||||
return emptyString;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>&
|
||||
SIPFmt::getBody()
|
||||
{
|
||||
return body_;
|
||||
}
|
||||
|
||||
void
|
||||
SIPFmt::swapBody(std::vector<uint8_t>& newBody)
|
||||
{
|
||||
body_.swap(newBody);
|
||||
}
|
||||
|
||||
void
|
||||
SIPFmt::swap(std::vector<uint8_t>& with)
|
||||
{
|
||||
if (not isValid_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
auto push_str = [&](const std::string& str) {
|
||||
for (auto it = str.cbegin(); it != str.cend(); ++it) {
|
||||
data.emplace_back((uint8_t) *it);
|
||||
}
|
||||
};
|
||||
|
||||
auto push_CRLN = [&] {
|
||||
data.emplace_back((uint8_t) '\r');
|
||||
data.emplace_back((uint8_t) '\n');
|
||||
};
|
||||
|
||||
if (isResponse()) {
|
||||
push_str(version_);
|
||||
data.emplace_back((uint8_t) ' ');
|
||||
push_str(status_);
|
||||
data.emplace_back((uint8_t) ' ');
|
||||
push_str(msg_);
|
||||
push_CRLN();
|
||||
} else {
|
||||
push_str(method_);
|
||||
data.emplace_back(' ');
|
||||
push_str(URI_);
|
||||
data.emplace_back(' ');
|
||||
push_str(version_);
|
||||
push_CRLN();
|
||||
}
|
||||
|
||||
setFieldValue("content-length", std::to_string(body_.size()));
|
||||
|
||||
for (auto it = fields_.cbegin(); it != fields_.cend(); ++it) {
|
||||
push_str(it->first);
|
||||
data.emplace_back((uint8_t) ':');
|
||||
data.emplace_back((uint8_t) ' ');
|
||||
|
||||
push_str(it->second);
|
||||
push_CRLN();
|
||||
}
|
||||
|
||||
push_CRLN();
|
||||
|
||||
for (auto it = body_.begin(); it != body_.end(); ++it) {
|
||||
data.emplace_back((uint8_t) *it);
|
||||
}
|
||||
|
||||
data.shrink_to_fit();
|
||||
|
||||
data.swap(with);
|
||||
}
|
73
test/fuzzing/lib/sip-fmt.h
Normal file
73
test/fuzzing/lib/sip-fmt.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class SIPFmt
|
||||
{
|
||||
public:
|
||||
SIPFmt(const std::vector<uint8_t>& data);
|
||||
|
||||
bool parse(const std::vector<uint8_t>& blob);
|
||||
|
||||
void pushBody(char *bytes, size_t len);
|
||||
|
||||
|
||||
const std::string& getField(const std::string& field) const ;
|
||||
const std::vector<uint8_t>& getBody();
|
||||
|
||||
void setVersion(const std::string& version) { version_ = version; }
|
||||
void setField(const std::string& field);
|
||||
void setFieldValue(const std::string& field, const std::string& value);
|
||||
void setMethod(const std::string& method) { method_ = method; }
|
||||
void setURI(const std::string& URI) { URI_ = URI; }
|
||||
void setStatus(const std::string& status) { status_ = status; }
|
||||
void setMsg(const std::string& msg) { msg_ = msg; }
|
||||
|
||||
void swapBody(std::vector<uint8_t>& body);
|
||||
|
||||
bool isValid() const { return isValid_; }
|
||||
bool isRequest() const { return isValid_ and isRequest_; }
|
||||
bool isResponse() const { return isValid_ and not isRequest_; }
|
||||
|
||||
bool isApplication(const std::string& app) const
|
||||
{
|
||||
return isValid() and (std::string::npos != getField("content-type").find("application/" + app));
|
||||
}
|
||||
|
||||
void setAsRequest() { isRequest_ = true; };
|
||||
void setAsResponse() { isRequest_ = false; };
|
||||
|
||||
void swap(std::vector<uint8_t>& with);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> body_ {};
|
||||
std::string status_ {};
|
||||
std::string version_ {};
|
||||
std::string msg_ {};
|
||||
std::string URI_ {};
|
||||
std::string method_ {};
|
||||
std::map<std::string, std::string> fields_ {};
|
||||
bool isValid_;
|
||||
bool isRequest_ {};
|
||||
};
|
177
test/fuzzing/lib/sip-parser.ll
Normal file
177
test/fuzzing/lib/sip-parser.ll
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Savoir-faire Linux Inc.
|
||||
*
|
||||
* Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "lib/sip-fmt.h"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc"
|
||||
#pragma GCC diagnostic ignored "-Wnull-dereference"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-overflow"
|
||||
%}
|
||||
|
||||
/* Definitions */
|
||||
%option 8bit
|
||||
%option warn
|
||||
%option always-interactive
|
||||
%option pointer
|
||||
%option reentrant
|
||||
%option noyywrap
|
||||
%option noyylineno
|
||||
%option nounput
|
||||
%option nodefault
|
||||
%option noinput
|
||||
%option debug
|
||||
%option prefix="sip_yy"
|
||||
%option extra-type="SIPFmt*"
|
||||
|
||||
%x RESPONSE_STATUS
|
||||
%x RESPONSE_MSG
|
||||
%x REQUEST_URI
|
||||
%x REQUEST_VERSION
|
||||
%x FIELD_SEQUENCE
|
||||
%x BODY
|
||||
|
||||
CRLF "\r\n"
|
||||
SIP_METHOD REGISTER|INVITE|ACK|CANCEL|BYE|OPTIONS|MESSAGE|INFO
|
||||
SIP_VERSION "SIP/2.0"
|
||||
SIP_STATUS [0-9]{3}
|
||||
SIP_MSG [^\r\n]*
|
||||
SIP_URI ("sip"|"sips")":"[^\r\n ]+
|
||||
FIELD_NAME ([a-zA-Z0-9]|[-_ ])+
|
||||
FIELD_VALUE [^\r\n]+
|
||||
|
||||
/* Rules */
|
||||
%%
|
||||
|
||||
/* Reponse line */
|
||||
<INITIAL>^{SIP_VERSION} {
|
||||
BEGIN(RESPONSE_STATUS);
|
||||
yyextra->setAsResponse();
|
||||
yyextra->setVersion(yytext);
|
||||
}
|
||||
|
||||
<INITIAL>^{SIP_METHOD} {
|
||||
BEGIN(REQUEST_URI);
|
||||
yyextra->setAsRequest();
|
||||
yyextra->setMethod(yytext);
|
||||
}
|
||||
|
||||
<REQUEST_URI>{SIP_URI} {
|
||||
BEGIN(REQUEST_VERSION);
|
||||
yyextra->setURI(yytext);
|
||||
}
|
||||
|
||||
<REQUEST_VERSION>{SIP_VERSION} {
|
||||
yyextra->setVersion(yytext);
|
||||
}
|
||||
|
||||
<RESPONSE_STATUS>{SIP_STATUS} {
|
||||
BEGIN(RESPONSE_MSG);
|
||||
yyextra->setStatus(yytext);
|
||||
}
|
||||
|
||||
<RESPONSE_MSG>{SIP_MSG} {
|
||||
yyextra->setMsg(yytext);
|
||||
}
|
||||
|
||||
<REQUEST_VERSION,RESPONSE_MSG>{CRLF} {
|
||||
BEGIN(FIELD_SEQUENCE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Field sequence
|
||||
*
|
||||
* We don't support multi-line field value.
|
||||
*/
|
||||
<FIELD_SEQUENCE>{
|
||||
|
||||
{FIELD_NAME}":"{FIELD_VALUE}{CRLF} {
|
||||
yytext[yyleng - 2] = '\0';
|
||||
yyextra->setField(yytext);
|
||||
yytext[yyleng - 2] = '\r';
|
||||
}
|
||||
|
||||
{CRLF} {
|
||||
BEGIN(BODY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<BODY>{
|
||||
|
||||
.+|\n {
|
||||
yyextra->pushBody(yytext, yyleng);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Don't care about spaces */
|
||||
<INITIAL,RESPONSE_MSG,RESPONSE_STATUS,REQUEST_VERSION,REQUEST_URI>[ \t]+ { }
|
||||
|
||||
<INITIAL,BODY><<EOF>> { return 0; }
|
||||
<RESPONSE_STATUS,RESPONSE_MSG,FIELD_SEQUENCE><<EOF>> { return -1; }
|
||||
|
||||
/* Default rule */
|
||||
<*>.|\n { return -1; }
|
||||
|
||||
%%
|
||||
|
||||
/* END LEX */
|
||||
|
||||
bool
|
||||
SIPFmt::parse(const std::vector<uint8_t>& blob)
|
||||
{
|
||||
yyscan_t scanner = NULL;
|
||||
int err;
|
||||
|
||||
YY_BUFFER_STATE state;
|
||||
|
||||
isValid_ = false;
|
||||
|
||||
if (sip_yylex_init_extra(this, &scanner)) {
|
||||
return isValid_;
|
||||
}
|
||||
|
||||
// sip_yyset_debug(1, scanner);
|
||||
|
||||
state = sip_yy_scan_bytes((const char*)blob.data(),
|
||||
blob.size(),
|
||||
scanner);
|
||||
|
||||
sip_yy_switch_to_buffer(state, scanner);
|
||||
|
||||
err = sip_yylex(scanner);
|
||||
|
||||
sip_yy_delete_buffer(state, scanner);
|
||||
sip_yylex_destroy(scanner);
|
||||
|
||||
if (err >= 0) {
|
||||
isValid_ = true;
|
||||
}
|
||||
|
||||
return isValid_;
|
||||
}
|
Reference in New Issue
Block a user