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

Xorg/net wm pid #7445

Merged
merged 8 commits into from Jun 17, 2018
Merged

Xorg/net wm pid #7445

merged 8 commits into from Jun 17, 2018

Conversation

thoughtjigs
Copy link
Contributor

@thoughtjigs thoughtjigs commented Jun 13, 2018

While working on a personal X window manager project I discovered that minetest does not support most of the Extended Window Manager Hints spec. This includes things like _NET_WM_PID which allows the discovery of the process underlying a window.

Providing _NET_WM_PID will allow window managers to terminate an unresponsive minetest window.

There is actually quite a bit of work that could be done to improve how minetest handles the additional Xorg stuff that Irrlicht doesn't support but for a start getting support for _NET_WM_PID is probably the easiest to achieve.

I have also created a backport of these changes to the 14.7.1 branch if that will be useful.

--EDIT--
Editing to add that xprop can be used at the CLI to verify that _NET_WM_PID and WM_CLIENT_MACHINE have been set correctly.

@SmallJoker SmallJoker added @ Startup / Config / Util Feature ✨ PRs that add or enhance a feature labels Jun 13, 2018

Atom NET_WM_PID = XInternAtom(x11_dpl,
"_NET_WM_PID",
false);
Copy link
Member

Choose a reason for hiding this comment

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

pretty sure this fits on a single line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, probably left over from longer variable names I was using.

(unsigned char *) &pid,1);

// According to the Extended Window Manager Hints spec if _NET_WM_PID
// is set then WM_CLIENT_MACHINE must also be set.
Copy link
Member

Choose a reason for hiding this comment

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

and if it isn't? do other applications set this value?
it would be nice to avoid the getaddrinfo stuff below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. I took a look at the GTK/GDK libraries to see how they handle it and they don't even attempt to set WM_CLIENT_MACHINE even though they do set _NET_WM_PID. The reason for providing a FQDN for the the WM_CLIENT_MACHINE is for that uniquely X situation where the X server may actually reside on a different machine from the X client and so the PID would be meaningless to the window manager (assuming the window manager was not running on the same machine as minetest).

For minetest this is very likely not an issue as I am pretty sure that the performance of minetest over the network (i.e. thin client) would not be the best.

There are several options for dealing with this ... the code could attempt to discover if the server and client are on the same machine and if they are not then don't set the _NET_WM_PID. Alternatively _NET_WM_PID could be set in all cases and then just let the window manager deal with it. The former would be useful but problems would only arise in the extreme edge case described above.

Are you aware of any users running minetest in such a scenario?

Copy link
Member

Choose a reason for hiding this comment

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

Not aware of anyone using Minetest over the network and it's certainly not a configuration we explicitly support or recommend.
I'd definitely prefer omitting support for WM_CLIENT_MACHINE entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would you prefer blindly setting _NET_WM_PID or would it make sense to attempt a few sanity checks before hand to ensure that X is not running in an unsupported fashion? I could see the sanity checks as being generally useful and a warning could be issued in the logs indicating that the user was attempting to run minetest in an unsupported fashion.

Copy link
Member

Choose a reason for hiding this comment

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

Former, since that matches the behaviour of e.g. GTK as you've said.

// the results of gethostname to determine the length.

size_t hostname_len=128;
char* hostname=0;
Copy link
Member

Choose a reason for hiding this comment

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

nullptr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, will fix

Copy link
Member

Choose a reason for hiding this comment

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

spaces around =

Copy link
Member

Choose a reason for hiding this comment

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

wrong pointer placement char *hostname

while (1) {
// (Re)allocate buffer of length hostname_len.
char* realloc_hostname=(char *) realloc(hostname,hostname_len);
if (realloc_hostname==0) {
Copy link
Member

Choose a reason for hiding this comment

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

nullptr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, will fix

if (gethostname(hostname,hostname_len-1)==0) {
size_t count=strlen(hostname);
if (count<hostname_len-2) {
// Break from loop if hostname definitely not truncated
Copy link
Member

Choose a reason for hiding this comment

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

instead of doing all this jazz to make sure it's 100% correct in all cases,
IMO in the sense of simplicity, just give gethostname 1024 bytes to work and hope it's enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I waffled back and forth between which solution was best. On the one hand I am pretty old school and prefer not to allocate memory where it is not needed but with modern day computers 1024 vs 128 is pretty insignificant.

An alternative would be to check to see if the hostname was longer than 1024 and just not set WM_CLIENT_MACHINE if it was.

A hostname longer than 1024 is extremely unlikely anyway.

I am fine with any of the options and only went with this option for efficiency and safety.


struct addrinfo hints={0};
hints.ai_family=AF_UNSPEC;
hints.ai_flags=AI_CANONNAME;
Copy link
Member

Choose a reason for hiding this comment

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

spacing around =

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will fix

<< res->ai_canonname << "'"
<< std::endl;

// Copy canonical name to new string so that addrinfo can be freed
Copy link
Member

Choose a reason for hiding this comment

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

copying it to a new string does not mean you have to use a std::string*

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this could be reworked. I started using std::string before I discovered the info|verbose|error|etc...streams. I think it would be sufficient to just use a char array.

I noticed that there is no consensus in the code on whether to use wchar_t or char for the log streams. Are there plans to internationalize the log messages?

Copy link
Member

Choose a reason for hiding this comment

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

No, log messages should use char.

// gethostname and getaddrinfo do not support IDN (thank the
// flying spaghetti monster) this means that fqdn will be represented
// in ASCII (any IDN names would be converted using punycode).
std::string *fqdn_ptr;
Copy link
Member

Choose a reason for hiding this comment

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

you forgot to delete this one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, will fix (or rework in light of no need for std::string)

// the results of gethostname to determine the length.

size_t hostname_len=128;
char* hostname=0;
Copy link
Member

Choose a reason for hiding this comment

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

spaces around =

@thoughtjigs
Copy link
Contributor Author

Updated the pull request with most of the requested changes.

I had a question about the use of std::string instead of std::string * for fqdn ... it occurs to me that not using a pointer and therefore new/delete that the client machines fqdn could potentially be present in memory after exiting the setXorgNetWMPID method. While it is unlikely that it won't get overwritten very soon after it does create a situation where code that should not have access to the users fqdn could maliciously obtain it. I am not sure if this includes lua scripts.

Is this a concern?

With regards to the code that gathers the hostname and fqdn I will defer to your collective judgement on the likely hood of minetest running on a thin client where minetest and the window manager are not on the same machine. I am comfortable only setting the _NET_WM_PID and either leaving the WM_CLIENT_MACHINE unset or only setting it to hostname.

As for discovery of the hostname I would prefer to use as little memory as possible but agree that creating a buffer with a size of 1024 will likely have no impact on modern day systems. As long as only 1023 bytes are offered to gethostname and the buffer is null terminated before calling gethostname there should be no issues.

@sfan5
Copy link
Member

sfan5 commented Jun 13, 2018

could potentially be present in memory after exiting the setXorgNetWMPID method

  1. Even if you don't use a pointer, instancing a std::string will allocate memory.
  2. The C++ standard library does not zero freed memory anyway.

Is this a concern?

If Lua script had ways to access uninitialised stack or heap space, we would have far worse problems. So, no.

@thoughtjigs
Copy link
Contributor Author

thoughtjigs commented Jun 13, 2018

If Lua script had ways to access uninitialised stack or heap space, we would have far worse problems. So, no.

@sfan5 , that is a good point.

In general has there been any concern about timing/cross channel attacks (in light of meltdown/spectre) with regards to untrusted mods? Webbrowsers limited time resolution of javascript in order to protect against attacks coming from untrusted javascript. It might make sense to do something similar in lua mods ... if there is enough freedom there to pull off such an attack.

This is to be more consistent with how gtk/gdk handles it as well as take advantage of some of the improvements to the RenderingEngine class.
@thoughtjigs
Copy link
Contributor Author

thoughtjigs commented Jun 14, 2018

Took another look at how gtk/gdk handles things See the setup_toplevel_window method.

It looks like gdk is setting the WM_CLIENT_MACHINE via the XSetWMProperties function. I believe this sets it to the hostname (as that is what the ICCM requires).

I reworked my changes to more closely follow what the gdk does. I also tried to take advantage of some of the improvements to the RenderingEngine class code and began the process of isolating Irrlicht. Ideally I think it would be better to have a RenderingEngine class for each unique env so that details specific to those envs would be available directly as class members e.g. XDisplay or XWindow but that can be left to a later date as it would be a significant change.

I also went ahead and set the WM_CLIENT_LEADER property using the same pattern that the gdk uses.

Verified that XSetWMProperties uses gethostname (if available) to set WM_CLIENT_MACHINE so it will not be a fully qualified domain name. See https://github.com/mirror/libX11/blob/master/src/WMProps.c and https://github.com/mirror/libX11/blob/master/src/XlibInt.c. It also limits it to 255 bytes in length.

Moved verbose messaging to setupXorgTopLevelWindow method as Xorg messages should only occur when
running in Xorg env.

Window x11_win = (Window)exposedData.OpenGLLinux.X11Window;
Copy link
Member

Choose a reason for hiding this comment

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

same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one may not be necessary. I will try to compile it without it.

if (video_data.OpenGLLinux.X11Display == NULL)
const video::SExposedVideoData exposedData = driver->getExposedVideoData();

Display *x11_dpl = (Display *)exposedData.OpenGLLinux.X11Display;
Copy link
Member

Choose a reason for hiding this comment

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

is this cast required at all? otherwise reinterpret_cast

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems to be. I get a compiler error when it not present. I will check and see what Irrlicht thinks it is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting ... returns it as a void * X11Display see: http://irrlicht.sourceforge.net/docu/structirr_1_1video_1_1_s_exposed_video_data.html

window is returned as an unsigned long which is probably ok as that is what it is under the hood in Xlib ... but it should probably be reinterpreted as a window type in case code further down expects it to be used in that way.

// method. But for now (as it would require some significant changes)
// leave the code as is.

// The following is borrowed from the above gdk source for setting top
Copy link
Member

Choose a reason for hiding this comment

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

should be tabs

Irrlicht returns the XDisplay as a void* and XWindow as an unsigned long so reinterpret those
as the appropriate type. Also fixed a spaces for tab formating issue
Copy link
Member

@SmallJoker SmallJoker left a comment

Choose a reason for hiding this comment

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

LGTM

@SmallJoker SmallJoker merged commit 3d51607 into minetest:master Jun 17, 2018
@thoughtjigs thoughtjigs deleted the xorg/net_wm_pid branch June 17, 2018 15:08
osjc pushed a commit to osjc/minetest that referenced this pull request Jan 23, 2019
Adding support for _NET_WM_PID as defined in Extended Window Manager Hints
Move verbose messaging to setupXorgTopLevelWindow method as Xorg messages should only occur when running in Xorg env.
Irrlicht returns the XDisplay as a void* and XWindow as an unsigned long so reinterpret those as the appropriate type. Also fixed a spaces for tab formating issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants