-
Notifications
You must be signed in to change notification settings - Fork 366
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
tcti/py: initial commit #2749
base: master
Are you sure you want to change the base?
tcti/py: initial commit #2749
Conversation
Ahh I'm not supposed to link explicitly to libpython I guess: https://peps.python.org/pep-0513/#libpythonx-y-so-1 No just gotta do it right... Using python3-confifg tool |
e970577
to
c8feee6
Compare
Woah, exciting! I was working on something similar, an out-of-tree Rust framework for writing TCTIs. It is already fully functional. However, it escalated a little, so I am currently working on marshalling/unmarshalling stuff... pub mod lib {
use tss2_tcti::define_api_symbols;
use tss2_tcti::tcti::error::TctiError;
use tss2_tcti::tcti::tcti::{Api, Info, State, Tcti, TctiLib};
use tss2_tcti::tctildr::tcti_loader::TctiLoader;
use tss2_tcti_sys::tpm2_tss;
#[repr(C)]
#[derive(Debug)]
pub struct TctiFoobar {
api: Api,
state: State,
child_tcti: Option<TctiLoader>,
}
impl TctiLib for TctiFoobar {
const INFO: Info<'static> = Info {
name: b"tpm2_tcti-foobar\0",
description: b"A demo tcti written in Rust.\0",
config_help: b"Child TCTI config string as expected by TctiLdr.\0",
};
const MAGIC: u64 = 0x44a50b8745675fe5;
fn new(conf: &str) -> Result<Self, TctiError> {
let mut tcti = Self {
api: Self::get_api_static(),
state: State::NotInitialized,
child_tcti: None,
};
tcti.init(conf)?;
Ok(tcti)
}
fn init_inner(&mut self, conf: &str) -> Result<(), TctiError> {
self.api = TctiFoobar::get_api_static();
self.child_tcti = Some(<TctiLoader as Tcti>::new(conf)?);
self.state = State::Transmit;
Ok(())
}
fn transmit_inner(&mut self, command: &[u8]) -> Result<(), TctiError> {
println!("TX: {:?}", command);
if let Some(child_tcti) = self.child_tcti.as_mut() {
return child_tcti.transmit(command);
}
todo!()
}
fn receive_inner(&mut self) -> Result<Vec<u8>, TctiError> {
if let Some(child_tcti) = self.child_tcti.as_mut() {
return child_tcti.receive();
}
todo!()
}
fn get_state(&self) -> Option<State> {
Some(self.state)
}
fn set_state(&mut self, state: State) {
self.state = state;
}
}
define_api_symbols!(TctiFoobar);
} |
src/tss2-tcti/tcti-py.c
Outdated
_global.refcnt = new_cnt; | ||
if (new_cnt == 1) { | ||
/* See: https://stackoverflow.com/questions/60719987/embedding-python-which-uses-numpy-in-c-doesnt-work-in-library-dynamically-loa */ | ||
_global.dlhandle = dlopen ("libpython3.so", RTLD_LAZY|RTLD_GLOBAL); |
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 do we not link libpython3.so
as a shared library? That would lead to compile-time errors if the libpython dependency is not installed (instead of runtime errors). Also, it would simplify the code.
EDIT: If i understand correctly, this is to enable multiple instances of tcti-py, right? In that case we would need dlopen()...
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.
We actually do link it, but if other bindings are in the link chain, like CFFI, it will resolve the symbols for that and leave some of the symbols unresolved, so we add GLOBAL and LAZY to force the resolution of undefined symbols at run time.
That comment above is what led me down this path, it seems to work as expected. I didn't find any other way to work around it.
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.
Hm, I'm not sure I have fully understood the issue. You mean because the python module uses tctildr?
Another thought: we need to get the pkg-config library name (for me "libpython3.10.so") via define, I think. For me, I had to change the hardcoded shared library name.
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.
Hm, I'm not sure I have fully understood the issue. You mean because the python module uses tctildr? Another
No it has to do with symbol resolution and the dynamic linker loader, not anything tss2 specific.
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.
we need to get the pkg-config library name (for me "libpython3.10.so") via define
Did that in the newest spin
src/tss2-tcti/tcti-py.c
Outdated
_global.refcnt = new_cnt; | ||
if (new_cnt == 1) { | ||
/* See: https://stackoverflow.com/questions/60719987/embedding-python-which-uses-numpy-in-c-doesnt-work-in-library-dynamically-loa */ | ||
_global.dlhandle = dlopen ("libpython3.so", RTLD_LAZY|RTLD_GLOBAL); |
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.
On my system (debian unstable) libpython3.so
doesn't exist, but one for each installed python version (libpython3.11.so.1.0
, etc), so tests fails with:
WARNING:tcti:src/tss2-tcti/tcti-py.c:442:Tss2_Tcti_Py_Init() Could not dlopen libpython3.so, some things may not work: libpython3.so: cannot open shared object file: No such file or directory
ModuleNotFoundError: No module named 'pytcti'
Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
Python runtime state: finalizing (tstate=0x00007ff374562978)
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.
Same... see my comment above.
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.
Should work now, I parse out the library name, the proper package to have is python3-embed, figuring that out for pkg-config helped a bit to simplify things.
py_error: | ||
PyErr_Print(); | ||
SAFE_DECREF(py_buf); |
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.
This isn't really important, but have you looked at using the tss logging infrastructure for the output of tracebacks? Or providing some API for the python
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.
It would take a lot of work to plumb everything I think, far more work than it's worth IMO. Unless I am missing something.
src/tss2-tcti/tcti-py.h
Outdated
PyObject *receive; | ||
PyObject *make_sticky; | ||
} methods; |
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.
make_sticky doesn't seem to be used and is marked as unsupported.
Also, would be set_locality be useful?
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.
Well I dropped these do to a weird bug, but I spent all day figuring it out, It was all from not clearing the error handler when get_attr() in the C code failed. Later I would get errors when calling the next Py_ but it was related to something N calls ago :-p. It was wierd that when you called transmit, it would say, no attribute named 'make_sticky'.
Works for me! LD_LIBRARY_PATH=:../tpm2-tss/src/tss2-tcti/.libs PYTHONPATH=/path/to/python/script/ TSS2_LOG=tcti+trace tpm2 startup -c --tcti="py:foobar:libtpms" When my python script errors during init (e.g. because I call it with args=None which the example script cannot handle), I get a seg fault, probably because we call |
Also, I made tpmstream (master branch, not released yet) a tcti like in your example. So you can do the following now: LD_LIBRARY_PATH=:../tpm2-tss/src/tss2-tcti/.libs tpm2 startup -c --tcti="py:tpmstream:libtpms" |
c8feee6
to
dbb61bd
Compare
Fixed that, it was from calling Py_Finalize() followed by a Py_XDECREF() |
Implmenet a TCTI module that will allow folks to write TCTIs in Python3. This will allow for more crazy and complex TCTI scenarios without having to re-write everyones stacks to use tpm2-pytss. Example Commandline: ```bash PYTHONPATH=$HOME tpm2_getcap --verbose --tcti=py:pytctitabrmd properties-fixed ``` Example TCTI: For the ultimate in mind-bending: C -> Python -> C and back for efficiency :-p. ```python3 from tpm2_pytss import TCTILdr from typing import Union class MyPyTCTI(object): def __init__(self, args: str): c = args.split(":", maxsplit=1) mod = c[0] args = c[1] if len(c) > 1 else "None" print(f"PYTHON: Initializing TCTI Ldr with mod: {mod} args: {args}") self._tcti = TCTILdr(mod, args) @Property def magic(self) -> Union[bytes, int]: # Optional Method print("PYTHON magic") return 42 def receive(self, timeout: int) -> bytes: print("PYTHON receive") return self._tcti.receive(timeout=timeout) def transmit(self, data: bytes) -> None: print("PYTHON transmit") return self._tcti.receive(timeout=timeout) def tcti_init(args: str) -> MyPyTCTI: print(f"PYTHON tcti_init called with: {args}") return MyPyTCTI(args) ``` Signed-off-by: Bill Roberts <bill.c.roberts+gh@gmail.com>
dbb61bd
to
5cc69ec
Compare
Building Fedora-34 with python3-devel, so once that is published I can add a test to the ci: |
Implmenet a TCTI module that will allow folks to write TCTIs in Python3. This will allow for more crazy and complex TCTI scenarios without having to re-write everyones stacks to use tpm2-pytss.
TODO:
Example Commandline:
Example TCTI:
For the ultimate in mind-bending: C -> Python -> C and back for efficiency :-p.