-
-
Notifications
You must be signed in to change notification settings - Fork 548
Add ability to run arbitrary commands on keypress #1843
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
base: main
Are you sure you want to change the base?
Conversation
|
There are a lot of feature suggestions of running arbitrary commands through htop, but almost all the pull request code suffers from a problem: htop is often run with elevated privileges (e.g. So this pull request doesn't have either security mechanism yet. |
| const char* path = String_cat(home, "/htop/run_script"); | ||
| FILE* file = fopen(path, "r"); | ||
| if (file) { | ||
| execl(path, path, NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| execl(path, path, NULL); | |
| execl(path, path, (void*)NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more thing. Since we need to verify the ownership of the run_script to be executed, the execl, execv family of API would not fit the job.
Consider the fexecve(3) or execveat(2) API instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thank you for the clarification and suggestions. I'll see if I'm able to implement something that fits #1844 to address this security issue.
| } else { | ||
| // check if htoprc has something | ||
| const char* htoprc_path = st->host->settings->scriptLocation; | ||
| execl(htoprc_path, htoprc_path, NULL); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| execl(htoprc_path, htoprc_path, NULL); | |
| execl(htoprc_path, htoprc_path, (void*)NULL); |
BenBE
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Were parts of this PR assisted or written aided by LLMs?
Also, as the command line of a process may contain anything, A more robust solution would provide the exact length of the data to follow and push the data after that literal. Arguments are split by \0 by the kernel, but since programs can overwrite that buffer anything goes …
Also, given that there are some other (similar) screens, what do you think of merging these (in a separate) PR to reduce code duplication?
| char pid[pid_length + 1]; | ||
| snprintf(pid, pid_length + 1, "%d", row->id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No VLA. Also use internal xSnprintf APIs. Also, why not allocate a static buffer that's large enough for any %d like 32 bytes? Or use multiple writes for the target FD?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I missed xSnprintf when I was going through xUtils, apologies.
I chose to do all these string concatenations and a singular write because I thought (although I have no data to back this up) a write is much more expensive than string operations, but I see how it makes the code messier. I'm open to switching to multiple writes though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mchlyu There is also xAsprintf if you need to allocate and print the format string in one call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh that might be useful, thanks for pointing that out!
| char* pid_str = String_cat(pid, "\t"); | ||
| char* user = String_cat(this->user, "\t"); | ||
| char* cmd = String_cat(Process_getCommand(this), "\n"); | ||
| char* user_and_cmd = String_cat(user, cmd); | ||
| char* line = String_cat(pid_str, user_and_cmd); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make this multiple writes into stdin of the new process instead? Apart from xSnprintf being the better tool for handling the %d\t%s\t part for PID+username here anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I said here, although I could be wrong or the performance difference doesn't matter that much or both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference performance-wise is one vs. multiple syscalls, but given that malloc might syscall itself (mmap), there is not much of a difference in practice. Also I don't consider this a hot path, thus there's no pressing need for optimizations there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, thanks for clarifying; multiple writes will probably be much cleaner here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can use a separator other than tab.
The problem is that the cmdline can contain almost any character, including tab. I know it could be a pain to securely parse a cmdline string when it contains special characters, but we probably have to pass the cmdline string securely to the run script.
| break; | ||
| } | ||
|
|
||
| if (res > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invert condition and continue to reduce level of indentation. Cf. XReadFile API for other places that need this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, removed one level of indentation so far.
Regarding xReadFile, is that meant to be a function in xUtils? Because I can't find it in that file nor does searching online yield anything so I'm a bit confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has been moved in the latest sources to Compat.c as readfd_internal with Compat_readfile and Compat_readfileat being official callers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks for pointing out the new location.
|
@BenBE Thanks for pointing out a more robust approach. Regarding the command line of the process, could you please elaborate on how would I get the exact length of the command line, since right now I take whatever For merging the screens, that sounds like a good idea to me, although I'm less clear on which screen(s) this one is similar enough to be combined with. Perhaps something like |
|
For merging the existing screens, the most likely candidates are the |
Addresses #506
All input to script is given on stdin as a sequence of "PID" "USER" "COMMAND" in TSV format.
Users can configure htoprc to provide a script path setting that will act as fallback if no file exists at
$XDG_CONFIG_HOME/htop/run_script.I tested functionality using this gdb script on Ubuntu.
Created new InfoScreen pager heavily inspired by
OpenFilesScreenandTraceScreento display stdout and stderr from script.Also updated help screen and man page to include this feature.
Valgrind did not report any new memory leaks.
I'm open to any suggestions for improving the structure, security, etc. of the code or clarity of the documentation.
This is my first contribution to this project (and open source in general), so please let me know if I’ve missed any conventions.