Bug 205317 - iopl(2) -privilege level is set per-process or per-thread?
Summary: iopl(2) -privilege level is set per-process or per-thread?
Status: RESOLVED CODE_FIX
Alias: None
Product: Documentation
Classification: Unclassified
Component: man-pages (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: documentation_man-pages@kernel-bugs.osdl.org
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-10-25 22:59 UTC by victorm007
Modified: 2020-06-30 07:51 UTC (History)
2 users (show)

See Also:
Kernel Version:
Tree: Mainline
Regression: No


Attachments
Example of threads and permissions in PMIO (1.11 KB, text/plain)
2020-05-24 10:26 UTC, Thomas Piekarski
Details

Description victorm007 2019-10-25 22:59:14 UTC
Current man page stipulates that iopl(2) "changes the I/O privilege level of the calling process...".

However , tglx says that this is wrong and that iopl is set per-thread, not per-process: https://lkml.org/lkml/2019/10/25/1057

Which is the correct version?
Comment 1 Thomas Piekarski 2020-05-24 10:22:48 UTC
IMHO iopl sets permission per thread, not per process.

Take a look at following code in which two threads are created and try to read some data with inb(). First thread (read_from_sleepy_child) is created before permissions are granted (but is delayed) and the second one (read_from_child) after that. 

If those permissions would be granted on process level should the first thread not succeed?

I hope I did not make any mistake in that code and applied threading well. I'd like to provide an update of this manual page stating that it is indeed like told at LKML - iopl, like ioperm sets permission on threads not on processes.


#include <pthread.h>
#include <stdio.h>
#include <sys/io.h>
#include <unistd.h>

#define PORT 0x378 // lp0

void *read_from_sleepy_child()
{
  sleep(3);

  // The inb() will fail due to missing permissions and it'll segfault
  // although permissions are acquired before threads are joined.
  // When permissions are set per process this should work.
  printf("Read anything from (sleepy) child thread (%x).\n", inb(PORT));

  return NULL;
}

void *read_from_child()
{
  // The inb() will succeed due to permissions are inherited to
  // childs after they got acquired with either iopl or ioperm
  printf("Read anything from child thread (%x).\n", inb(PORT));

  return NULL;
}

int main()
{
  pthread_t delayed_thread, thread;

  pthread_create(&delayed_thread, NULL, read_from_sleepy_child, NULL);

  iopl(3);
  // ioperm(0, 0xFFFF, 1);

  // The inb() will succeed due to being the main, default thread
  // where permissions got acquired in first place
  printf("Read anything from main thread (%x).\n", inb(PORT));

  pthread_create(&thread, NULL, read_from_child, NULL);
  pthread_join(delayed_thread, NULL);
  pthread_join(thread, NULL);

  return 0;
}
Comment 2 Thomas Piekarski 2020-05-24 10:26:51 UTC
Created attachment 289263 [details]
Example of threads and permissions in PMIO
Comment 3 Michael Kerrisk 2020-06-30 07:51:11 UTC
I think this issue is resolved thanks to Thomas Piekarski's patch below, so I am closing this report.

commit 6caf6c4380f2c6d706c3df6dcf2d8c3b9438d724
Author: Thomas Piekarski <t.piekarski@deloquencia.de>
Date:   Fri Jun 26 22:29:23 2020 +0200

    iopl.2: Updating description of permissions and disabling interrupts
    
    Update description of permissions for port-mapped I/O set
    per-thread and not per-process. Mention that iopl() can not
    disable interrupts since Linux 5.5 anymore and is in general
    deprecated and only provided for legacy X servers.
    

diff --git a/man2/iopl.2 b/man2/iopl.2
index e5b216a14..be9acfd1e 100644
--- a/man2/iopl.2
+++ b/man2/iopl.2
@@ -39,29 +39,17 @@ iopl \- change I/O privilege level
 .BI "int iopl(int " level );
 .SH DESCRIPTION
 .BR iopl ()
-changes the I/O privilege level of the calling process,
+changes the I/O privilege level of the calling thread,
 as specified by the two least significant bits in
 .IR level .
 .PP
-This call is necessary to allow 8514-compatible X servers to run under
-Linux.
-Since these X servers require access to all 65536 I/O ports, the
-.BR ioperm (2)
-call is not sufficient.
+The I/O privilege level for a normal thread is 0.
+Permissions are inherited from parents to children.
 .PP
-In addition to granting unrestricted I/O port access, running at a higher
-I/O privilege level also allows the process to disable interrupts.
-This will probably crash the system, and is not recommended.
-.PP
-Permissions are not inherited by the child process created by
-.BR fork (2)
-and are not preserved across
-.BR execve (2)
-(but see NOTES).
-.PP
-The I/O privilege level for a normal process is 0.
-.PP
-This call is mostly for the i386 architecture.
+This call is deprecated, significantly slower than
+.BR ioperm(2)
+and is only provided for older X servers which require
+access to all 65536 I/O ports. It is mostly for the i386 architecture.
 On many other architectures it does not exist or will always
 return an error.
 .SH RETURN VALUE
@@ -79,7 +67,7 @@ is greater than 3.
 This call is unimplemented.
 .TP
 .B EPERM
-The calling process has insufficient privilege to call
+The calling thread has insufficient privilege to call
 .BR iopl ();
 the
 .B CAP_SYS_RAWIO
@@ -99,6 +87,12 @@ and in
 .IR <sys/perm.h> .
 Avoid the latter, it is available on i386 only.
 .PP
+Prior to Linux 5.5
+.BR iopl ()
+allowed the thread to disable interrupts while running
+at a higher I/O privilege level. This will probably crash
+the system, and is not recommended.
+.PP
 Prior to Linux 3.7,
 on some architectures (such as i386), permissions
 .I were

Note You need to log in before you can comment on or make changes to this bug.