Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix interrupted scans #832

Merged
merged 7 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Deprecated
### Removed
### Fixed
Fix interrupted scan, when the process table is full. [#832](https://github.com/greenbone/openvas-scanner/pull/832)

[21.4.3]: https://github.com/greenbone/openvas-scanner/compare/v21.4.2...gvmd-21.04

Expand Down
34 changes: 25 additions & 9 deletions src/attack.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
#include <unistd.h> /* for close() */

#define ERR_HOST_DEAD -1
#define ERR_CANT_FORK -2

#define MAX_FORK_RETRIES 10
/**
Expand Down Expand Up @@ -346,13 +345,14 @@ check_new_vhosts (void)
* Does not launch a plugin twice if !save_kb_replay.
*
* @return ERR_HOST_DEAD if host died, ERR_CANT_FORK if forking failed,
* 0 otherwise.
* ERR_NO_FREE_SLOT if the process table is full, 0 otherwise.
*/
static int
launch_plugin (struct scan_globals *globals, struct scheduler_plugin *plugin,
struct in6_addr *ip, GSList *vhosts, kb_t kb, kb_t main_kb)
{
int optimize = prefs_get_bool ("optimize_test"), pid, ret = 0;
int optimize = prefs_get_bool ("optimize_test");
int launch_error, pid, ret = 0;
char *oid, *name, *error = NULL, ip_str[INET6_ADDRSTRLEN];
nvti_t *nvti;

Expand Down Expand Up @@ -428,11 +428,13 @@ launch_plugin (struct scan_globals *globals, struct scheduler_plugin *plugin,

/* Update vhosts list and start the plugin */
check_new_vhosts ();
pid = plugin_launch (globals, plugin, ip, vhosts, kb, main_kb, nvti);
if (pid < 0)
launch_error = 0;
pid = plugin_launch (globals, plugin, ip, vhosts, kb, main_kb, nvti,
&launch_error);
if (launch_error == ERR_NO_FREE_SLOT || launch_error == ERR_CANT_FORK)
{
plugin->running_state = PLUGIN_STATUS_UNRUN;
ret = ERR_CANT_FORK;
ret = launch_error;
goto finish_launch_plugin;
}

Expand Down Expand Up @@ -519,19 +521,33 @@ attack_host (struct scan_globals *globals, struct in6_addr *ip, GSList *vhosts,
kb_item_push_str (main_kb, "internal/results", buffer);
goto host_died;
}
else if (e == ERR_NO_FREE_SLOT)
{
if (forks_retry < MAX_FORK_RETRIES)
{
forks_retry++;
g_warning ("Launch failed for %s. No free slot available "
jjnicola marked this conversation as resolved.
Show resolved Hide resolved
"in the internal process table for starting a "
"plugin.",
plugin->oid);
fork_sleep (forks_retry);
goto again;
}
}
else if (e == ERR_CANT_FORK)
{
if (forks_retry < MAX_FORK_RETRIES)
{
forks_retry++;
g_debug ("fork() failed - sleeping %d seconds (%s)",
forks_retry, strerror (errno));
g_warning (
"fork() failed for %s - sleeping %d seconds (%s)",
plugin->oid, forks_retry, strerror (errno));
fork_sleep (forks_retry);
goto again;
}
else
{
g_debug ("fork() failed too many times - aborting");
g_warning ("fork() failed too many times - aborting");
goto host_died;
}
}
Expand Down
41 changes: 32 additions & 9 deletions src/pluginlaunch.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ simult_ports (const char *oid, const char *next_oid)
/**
* If another NVT with same port requirements is running, wait.
*
* @return -1 if MAX_PROCESSES are running, the index of the first free "slot"
* in the processes array otherwise.
* @return ERR_NO_FREE_SLOT if MAX_PROCESSES are running, the index of the first
* free "slot" in the processes array otherwise.
*/
static int
next_free_process (kb_t kb, struct scheduler_plugin *upcoming)
Expand All @@ -262,7 +262,7 @@ next_free_process (kb_t kb, struct scheduler_plugin *upcoming)
for (r = 0; r < MAX_PROCESSES; r++)
if (processes[r].pid <= 0)
return r;
return -1;
return ERR_NO_FREE_SLOT;
}

void
Expand Down Expand Up @@ -388,21 +388,35 @@ check_sysload ()
}

/**
* @brief Start a plugin.
*
* Check for free slots available in the process table. Set error with
* ERR_NO_FREE_SLOT if the process table is full. Set error with ERR_CANT_FORK
* if was not possible to fork() a new child.
*
* @return PID of process that is connected to the plugin as returned by plugin
* classes pl_launch function (<=0 means there was a problem).
* classes pl_launch function. Less than 0 means there was a problem,
* but error param should be checked.
*/
int
plugin_launch (struct scan_globals *globals, struct scheduler_plugin *plugin,
struct in6_addr *ip, GSList *vhosts, kb_t kb, kb_t main_kb,
nvti_t *nvti)
nvti_t *nvti, int *error)
{
int p;

/* Wait for a free slot */
pluginlaunch_wait_for_free_process (main_kb);
p = next_free_process (main_kb, plugin);
if (p < 0)
return -1;
{
g_warning ("%s. There is currently no free slot available for starting a "
"new plugin.",
__func__);
*error = ERR_NO_FREE_SLOT;
return -1;
}

processes[p].plugin = plugin;
processes[p].timeout = plugin_timeout (nvti);
gettimeofday (&(processes[p].start), NULL);
Expand All @@ -412,8 +426,10 @@ plugin_launch (struct scan_globals *globals, struct scheduler_plugin *plugin,
if (processes[p].pid > 0)
num_running_processes++;
else
processes[p].plugin->running_state = PLUGIN_STATUS_UNRUN;

{
processes[p].plugin->running_state = PLUGIN_STATUS_UNRUN;
*error = ERR_CANT_FORK;
}
return processes[p].pid;
}

Expand Down Expand Up @@ -461,8 +477,15 @@ pluginlaunch_wait_for_free_process (kb_t kb)
update_running_processes (kb);
/* Max number of processes are still running, wait for a child to exit or
* to timeout. */

if (num_running_processes >= max_running_processes)
g_debug ("%s. Number of running processes >= maximum running processes (%d "
">= %d). "
"Waiting for free slot for processes.",
__func__, num_running_processes, max_running_processes);

while (
(num_running_processes == max_running_processes)
(num_running_processes >= max_running_processes)
|| (num_running_processes > 0 && (check_memory () || check_sysload ())))
{
sigset_t mask;
Expand Down
11 changes: 10 additions & 1 deletion src/pluginlaunch.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
#include "pluginload.h" /* for struct pl_class_t */
#include "pluginscheduler.h" /* for struct plugins_scheduler_t */

/**
* @brief Error for when it is not possible to fork a new plugin process.
*/
#define ERR_CANT_FORK -2
/**
* @brief Error for when the process table is full
*/
#define ERR_NO_FREE_SLOT -99

void
pluginlaunch_init (const char *);
void pluginlaunch_wait (kb_t);
Expand All @@ -39,7 +48,7 @@ pluginlaunch_stop (void);

int
plugin_launch (struct scan_globals *, struct scheduler_plugin *,
struct in6_addr *, GSList *, kb_t, kb_t, nvti_t *);
struct in6_addr *, GSList *, kb_t, kb_t, nvti_t *, int *);

void
pluginlaunch_disable_parallel_checks (void);
Expand Down