From f94f366cf90f75d88229c68f7ec8951ef82cf2bf Mon Sep 17 00:00:00 2001 From: Philipp Maier Date: Tue, 28 Oct 2025 13:39:35 +0100 Subject: [PATCH] runtime: check record/file size before write When writing data to a transparent or linear fixed (record oriented) and the data to write exceeds the record/file size, then the UICC will respond with an error "6700: Checking errors - Wrong length" In particular when the data is supplied as a JSON object and not as a hex string, it may not be immediately obvious to the average user what the problem actually is. Let's check the record/file size before writing the data and raise an exception in case the data excieeds the record/file size. Let's also print an informative string message in case the data length is less than the record/file size to make the user aware of unwritten bytes at the end of a record/file. Related: OS#6864 Change-Id: I7fa717d803ae79398d2c5daf92a7336be660c5ad --- pySim/runtime.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pySim/runtime.py b/pySim/runtime.py index ab21912f..597323fb 100644 --- a/pySim/runtime.py +++ b/pySim/runtime.py @@ -514,6 +514,47 @@ class RuntimeLchan: dec_data = self.selected_file.decode_hex(data) return (dec_data, sw) + def __get_writeable_size(self): + """ Determine the writable size (file or record) using the cached FCP parameters of the currently selected + file. Return None in case the writeable size cannot be determined (no FCP available, FCP lacks size + information). + """ + fcp = self.selected_file_fcp + if not fcp: + return None + + structure = fcp.get('file_descriptor', {}).get('file_descriptor_byte', {}).get('structure') + if not structure: + return None + + if structure == 'transparent': + return fcp.get('file_size') + elif structure == 'linear_fixed': + return fcp.get('file_descriptor', {}).get('record_len') + else: + return None + + def __check_writeable_size(self, data_len): + """ Guard against unsuccessful writes caused by attempts to write data that exceeds the file limits. """ + + writeable_size = self.__get_writeable_size() + if not writeable_size: + return + + if isinstance(self.selected_file, TransparentEF): + writeable_name = "file" + elif isinstance(self.selected_file, LinFixedEF): + writeable_name = "record" + else: + writeable_name = "object" + + if data_len > writeable_size: + raise TypeError("Data length (%u) exceeds %s size (%u) by %u bytes" % + (data_len, writeable_name, writeable_size, data_len - writeable_size)) + elif data_len < writeable_size: + log.warn("Data length (%u) less than %s size (%u), leaving %u unwritten bytes at the end of the %s" % + (data_len, writeable_name, writeable_size, writeable_size - data_len, writeable_name)) + def update_binary(self, data_hex: str, offset: int = 0): """Update transparent EF binary data. @@ -524,6 +565,7 @@ class RuntimeLchan: if not isinstance(self.selected_file, TransparentEF): raise TypeError("Only works with TransparentEF, but %s is %s" % (self.selected_file, self.selected_file.__class__.__mro__)) + self.__check_writeable_size(len(data_hex) // 2 + offset) return self.scc.update_binary(self.selected_file.fid, data_hex, offset, conserve=self.rs.conserve_write) def update_binary_dec(self, data: dict): @@ -571,6 +613,7 @@ class RuntimeLchan: if not isinstance(self.selected_file, LinFixedEF): raise TypeError("Only works with Linear Fixed EF, but %s is %s" % (self.selected_file, self.selected_file.__class__.__mro__)) + self.__check_writeable_size(len(data_hex) // 2) return self.scc.update_record(self.selected_file.fid, rec_nr, data_hex, conserve=self.rs.conserve_write, leftpad=self.selected_file.leftpad)