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

Fixes: CFE-3982 do not kill unrelated process #4957

Closed
wants to merge 7 commits into from

Conversation

peckpeck
Copy link
Contributor

@olehermanse olehermanse requested a review from vpodzime May 24, 2022 10:38
Copy link
Contributor

@vpodzime vpodzime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please read our contributing guidelines and try to keep the same coding style as the surrounding code.

@@ -903,15 +927,23 @@ CfLock AcquireLock(EvalContext *ctx, const char *operand, const char *host,
"Lock expired after %jd/%u minutes: %s",
(intmax_t) elapsedtime, expireafter, cflock);

if (KillLockHolder(cflock))
if (is_cfengine_process(cflock.pid))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a function that returns the path/name to/of the executable when given a PID and using that function here. A the function should go to the file where we do process handling and process table parsing. Also, it should use the process info parsing code from there instead of reading /proc directly because /proc is not a portable thing available everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any function for reading information for a process given its pid in ./libpromises/processes_select.c
If this is not what you call "the file where we do process handling" please point me to the correct one.

@peckpeck
Copy link
Contributor Author

About the coding style, is there anything else than the function name that you want to point me to ?

@peckpeck peckpeck force-pushed the kill_cfengine_only branch from 7bb5648 to 396cf6a Compare May 30, 2022 14:52
@olehermanse
Copy link
Member

About the coding style, is there anything else than the function name that you want to point me to ?

@peckpeck Some things I see at first glance:

  1. Space after if, commas, and around operators (like >).
  2. Opening curly brace on separate line ({).
  3. Don't do if's without curly braces.
  4. Avoid unnecessary typecasts.
  5. Pointer asterisk to the right, next to the name (FILE *f).
  6. Compare pointers to NULL explicitly (if (f != NULL)).

char* procfile = xcalloc(1024, sizeof(char));
char* cmd = xcalloc(1024, sizeof(char));
if (cmd && procfile){
sprintf(procfile, "/proc/%d/cmdline", pid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This emits an error during compilation:

locks.c:565:9: error: 'sprintf' is deprecated

      [-Werror,-Wdeprecated-declarations]

        sprintf(procfile, "/proc/%d/cmdline", pid);

        ^

./../libntech/libutils/deprecated.h:42:5: note: 'sprintf' has been explicitly

      marked deprecated here

    FUNC_DEPRECATED("Try snprintf() or xsnprintf() or xasprintf()");

    ^

./../libntech/libutils/compiler.h:44:49: note: expanded from macro

      'FUNC_DEPRECATED'

#    define FUNC_DEPRECATED(msg) __attribute__((deprecated))

                                                ^

Copy link
Contributor

@vpodzime vpodzime left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit more work needed.

// There is no /proc on windows
#ifndef _WIN32
char* procfile = xcalloc(1024, sizeof(char));
char* cmd = xcalloc(1024, sizeof(char));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either free() these variables or, better, use arrays like char procfile[PATH_MAX] and similar.

#ifndef _WIN32
char* procfile = xcalloc(1024, sizeof(char));
char* cmd = xcalloc(1024, sizeof(char));
if (cmd && procfile){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to check these, xcalloc() kills the process in case of ENOMEM.

@@ -587,6 +616,12 @@ static bool KillLockHolder(const char *lock)

CloseLock(dbp);

if (!IsCfengineProcess(lock_data.pid)) {
Log(LOG_LEVEL_INFO,
"Lock holder disappeared");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This log message should be a bit more verbose and logged as VERBOSE.

@peckpeck
Copy link
Contributor Author

sorry for the delay

@peckpeck
Copy link
Contributor Author

@vpodzime any news on this ?

@larsewi
Copy link
Contributor

larsewi commented Dec 14, 2022

Hi @peckpeck ! as far as i know, Windows is not the only platform without the /proc file. Please correct me if I'm wrong. In libpromises/process_select.h we have some functions for querying processes. Maybe you can use those? E.g.

ClearProcessTable(); /* Make sure old process table is not cached */

Item *procs = NULL;
if (LoadProcessTable())
{
    ProcessSelect ps = PROCESS_SELECT_INIT;
    ps.max_pid = ps.min_pid = lock_data.pid;
    procs = SelectProcesses("cf-.*", &ps, true);
}
if (procs == NULL)
{
    /* Process select returned nothing, it must be an unrelated process */
    return false;
}
DeleteItemList(procs);
return true;

Not sure if this would work, but feel free to give it a try. Otherwise you could maybe guard it with the #ifdef __linux__ macro.

@@ -554,6 +554,33 @@ static void PromiseTypeString(char *dst, size_t dst_size, const Promise *pp)
}
}

// returns true if pid points to a cfengine process
static bool IsCfengineProcess(const int pid)
Copy link
Contributor

@vpodzime vpodzime May 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const has no meaning here, and the type should be pid_t not int.

FILE *f = fopen(procfile, "r");
if (f != NULL){
size_t size;
size = fread(cmd, sizeof(char), PATH_MAX, f);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check FullRead() in libntech/libutils

fclose(f);
// cmd contains the process command path
char *name = basename(cmd);
return (strncmp("cf-", name, 3) == 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check StringStartsWith() in libntech/libutils

}
fclose(f);
// cmd contains the process command path
char *name = basename(cmd);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If size is 0 then cmd is uninitialized here.

}
#endif
// assume true if we don't know
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be safer to assume the opposite?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't know means use the old behaviour on other platforms

if (!IsCfengineProcess(lock_data.pid)) {
Log(LOG_LEVEL_VERBOSE,
"Lock holder with pid %d was replaced by a non cfengine process, do not try to kill it!, ", lock_data.pid);
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the function returns true here in this case, but it doesn't really kill the lock holder. That can be a source of issues.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still applicable.

@peckpeck
Copy link
Contributor Author

PR updated, sorry for the delay

{
char procfile[PATH_MAX];
char cmd[PATH_MAX]; // we don't need the full ARG_MAX
snprintf(procfile, PATH_MAX, "/proc/%d/cmdline", pid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pid_t doesn't have to be int and this can easily fail to compile in such a case because of the format (%d) and type (pid_t) mismatch. We usually use a trick with uintmax_t for things like this.

// this will just return null on platform where /proc doesn't exist and return true as before
if (f != NULL){
size_t size;
size = FullRead(f, cmd, PATH_MAX);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size_t size = FullRead(f, cmd, PATH_MAX);

if (!IsCfengineProcess(lock_data.pid)) {
Log(LOG_LEVEL_VERBOSE,
"Lock holder with pid %d was replaced by a non cfengine process, do not try to kill it!, ", lock_data.pid);
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still applicable.

@vpodzime
Copy link
Contributor

vpodzime commented Nov 7, 2023

@peckpeck I took your code as a base for my work on the related ticket. Thank you for your contribution!

@vpodzime vpodzime closed this Nov 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants