Меню

Pickle data was truncated ошибка

🐛 Bug

I am trying to load my monodatasets for XLM and am stumped with this pickle data issue. I have attempted to pass various arguments including expression ascii, latin1 and utf-8
data = torch.load(path) File "libsite-packagestorchserialization.py", line 358, in load return _load(f, map_location, pickle_module) File "libsite-packagestorchserialization.py", line 532, in _load magic_number = pickle_module.load(f) _pickle.UnpicklingError: pickle data was truncated

To Reproduce

I am working with 0.4 pytorch on the recent (translation model)[https://github.com/facebookresearch/XLM/]

Environment

PyTorch version: N/A
Is debug build: N/A
CUDA used to build PyTorch: N/A

OS: Microsoft Windows 10 Pro
GCC version: Could not collect
CMake version: Could not collect

Python version: 3.5
Is CUDA available: N/A
CUDA runtime version: 9.0
GPU models and configuration: GPU 0: GeForce GTX 960M
Nvidia driver version: 419.35
cuDNN version: C:Program FilesNVIDIA GPU Computing ToolkitCUDAv9.0bincudnn64_7.dll

Versions of relevant libraries:
[pip] numpy==1.16.2
[pip] torch==0.4.1
[conda] blas 1.0 mkl
[conda] cuda90 1.0 0 pytorch
[conda] mkl 2019.1 144
[conda] mkl 2019.0
[conda] mkl_fft 1.0.10 py36h14836fe_0
[conda] mkl_random 1.0.2 py36h343c172_0
[conda] pytorch 0.4.1 py36_cuda90_cudnn7he774522_1 pytorch

Additional context

#python-3.x #macos #sockets #pickle

Вопрос:

Я пытаюсь отправить данные со своей локальной машины под управлением Big Sur 11.2.3 на удаленный сервер.

server.py

 import socket, pickle
import env

print("Server is listening on port %s..." % env.PORT)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((env.HOST, env.PORT))
s.listen(1)
conn, addr = s.accept()
print("Connected to %s" % str(addr))

data = []
while True:
    packet = conn.recv(1024)
    if not packet: break
    data.append(packet)
data = pickle.loads(b"".join(data))
print(data)
conn.close()
 

client.py

 import socket, pickle
import env

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((env.HOST, env.PORT))

variable = ['a', 'b', 'c', 'd'] * (2**14)

data_string = pickle.dumps(variable)
s.send(data_string)

s.close()
print('Data sent to server')
 

Первоначально это работало при использовании variable = ['a', 'b', 'c', 'd'] , и для имитации отправки большего объекта я просто умножил его на большое число. Первоначально я думал, что это не сработает, если данные будут больше, чем количество байтов, которое я указывал серверу для получения в качестве каждого пакета, но этот код работает до тех пор, пока я не умножу на 2**14 (используя 2**13 works).

Самое странное, что если я попробую это с моего Raspberry Pi (то есть отправлю данные с pi на удаленный сервер), все будет нормально. Но когда я делаю то же самое со своего macbook, я получаю

 Traceback (most recent call last):
  File "server.py", line 17, in <module>
    data = pickle.loads(b"".join(data))
_pickle.UnpicklingError: pickle data was truncated
 

В чем может быть причина этой неспособности работать на macOS?
Если я попытаюсь сделать это локально (то есть запустить сервер и экземпляр клиента как на моем macbook), он будет работать нормально, но не сработает, когда я попытаюсь отправить с mac на другую машину (также не получится, когда я попытаюсь отправить с mac на pi).

Я читал, что возможны проблемы, когда машины, отправляющие и получающие маринованные данные, работают под управлением разных версий python, но я не верю, что здесь проблема в этом. Удаленный сервер работает под управлением python 3.6.9, а мой pi работает под управлением python 3.7.3. Чтобы убедиться, я запустил сценарий на своем macbook в виртуальной среде env под управлением 3.6.9, и он все равно не удался.

Ответ №1:

 s.send(data_string)
 

Вам повезло, что он выходит из строя только на macOS. send не гарантирует отправку всех данных, но вы должны проверить, сколько на самом деле было отправлено, и позвонить send еще раз (и, возможно, еще раз) для отправки остальных. Или использовать sendall .

Из документации:

Возвращает количество отправленных байтов. Приложения отвечают за проверку того, что все данные были отправлены; если была передана только часть данных, приложению необходимо попытаться доставить оставшиеся данные.

Почему send он ведет себя по-разному на разных платформах или в разных средах? Потому send что просто помещает данные в буфер отправки сокетов. Буфер имеет ограниченный размер, который может зависеть от операционной системы, конфигурации операционной системы, приложения, текущего состояния системы (т. Е. от нехватки памяти или нет) … . Сингл send поместит в буфер отправки только столько данных, сколько в настоящее время там свободного места. Если буфер заполнен, он будет блокироваться до тех пор, пока не освободится некоторое пространство. Но это не гарантирует, что все можно поместить в буфер сокета.

published on Monday, December 21, 2020

You are probably aware that pickle.load can execute arbitrary code and
must not be used for untrusted data.

This post is not about that.

In fact, pickle.load can’t even really be trusted for trusted data. To
demonstrate the issue, consider this simple program:

import os, pickle, threading

message = '0' * 65537

recv_fd, send_fd = os.pipe()
read_end = os.fdopen(recv_fd, 'rb', 0)
write_end = os.fdopen(send_fd, 'wb', 0)

thread = threading.Thread(
    target=pickle.dump,
    args=(message, write_end, -1))
thread.start()

pickle.load(read_end)

This simply transmits a pickled message over a pipe over a pipe.
Looks innocuous enough, right?

Wrong! The program fails with the following traceback every time:

Traceback (most recent call last):
  File "<...>/example.py", line 16, in <module>
    pickle.load(read_end)
_pickle.UnpicklingError: pickle data was truncated

Worse: once you get this error, there is safe way to resume listening for
messages on this channel, because you don’t know how long the first message
really was, and hence, at which offset to resume reading. If you try this, you
invite evil into your home. A typical result of trying to continue reading
messages on the stream may be _pickle.UnpicklingError: unpickling stack
underflow, but I’ve even seen segfaults occur.

The reason that we get the error in the first place is of course that the
message size above the pipe capacity, which is 65,536 on my system. The
threshold at which you start getting errors may of course be different for
you. Try increasing the message size if you don’t see errors at first.

If you are using a channel other than os.pipe(), you might be safe – but I
can’t give any guarantees on that. I just can say that I wasn’t able to
reproduce the error on my system when exchanging the pipe for a socket or
regular file.

We used a thread here to send us the data, but it doesn’t matter if the remote
end is a thread or another process. Also, this is not limited to a specific
python version, or version of the pickle protocol. I could reproduce the same
error with several python versions up to python 3.9, and protocols 1-5.

Workaround

So, how to fix that?

The problem empirically seems to disappear when changing the buffering policy
of the reading end, i.e. by not disabling input buffering:

- read_end = os.fdopen(recv_fd, 'rb', 0)
+ read_end = os.fdopen(recv_fd, 'rb')

I haven’t inspected the source of the pickle module, so I can’t vouch that
this is reliable.

What I turned out doing is to use the pickle.dumps()/pickle.loads()
combination to serialize to/from a bytes object, and manually transmit this
data along with its size over the channel. This has some overhead, but still
performs fine for my use-case:

import pickle
from struct import Struct

HEADER = Struct("!L")

def send(obj, file):
    """Send a pickled message over the given channel."""
    payload = pickle.dumps(data, -1)
    file.write(HEADER.pack(len(payload)))
    file.write(payload)

def recv(file):
    """Receive a pickled message over the given channel."""
    header = read_file(file, HEADER.size)
    payload = read_file(file, *HEADER.unpack(header))
    return pickle.loads(payload)

def read_file(file, size):
    """Read a fixed size buffer from the file."""
    parts = []
    while size > 0:
        part = file.read(size)
        if not part:
            raise EOFError
        parts.append(part)
        size -= len(part)
    return b''.join(parts)

Technically, transmitting the size is redundant with information contained in
the pickle protocol. However, where excessive performance is not an issue
(remember: we are using python, after all), I prefer transmitting the size
explicitly anyway. This evades the complexity of manually interacting with the
pickled frames, avoids dependency on a specific pickle protocol, and would
also make it easy to exchange pickle for any other serialization format here.

Conclusion

Be careful with using pickle.dump + pickle.load for RPC. It may result
in an UnpicklingError from which there seems to be no safe way of recovery
that allows to continue transmitting further messages on the same channel.
This occurs when the message size exceeds a certain threshold.

To avoid this issue, make sure that the channel capacity and buffering policy
works with pickle.load. Alternatively, consider using pickle.dumps +
pickle.loads, and handling the channel layer manually instead.

This entry was tagged

bug,
coding and
python

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Pick up card ошибка
  • Pioneer vsx c300 ошибка