diff --git a/platform/linux-generic/odp_crypto.c b/platform/linux-generic/odp_crypto.c
index a147f271..e5468c40 100644
--- a/platform/linux-generic/odp_crypto.c
+++ b/platform/linux-generic/odp_crypto.c
@@ -195,15 +195,101 @@ odp_crypto_alg_err_t auth_check(odp_crypto_op_param_t *param,
 	return ODP_CRYPTO_ALG_ERR_NONE;
 }
 
+#define IN_CONSUME(len)		\
+	in_pos += (len);	\
+	in_len -= (len);	\
+	insegaddr += (len);	\
+	inseglen -= (len);
+
+static
+int internal_encrypt(EVP_CIPHER_CTX *ctx, odp_crypto_op_param_t *param)
+{
+	odp_packet_t pkt = param->out_pkt;
+	unsigned in_pos = param->cipher_range.offset;
+	unsigned out_pos = param->cipher_range.offset;
+	unsigned in_len = param->cipher_range.length;
+	uint8_t block[2 * EVP_MAX_BLOCK_LENGTH];
+	unsigned block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
+	int cipher_len;
+	int ret;
+
+	while (in_len > 0) {
+		uint32_t seglen = 0; /* GCC */
+		uint8_t *insegaddr = odp_packet_offset(pkt, in_pos, &seglen, NULL);
+		unsigned inseglen = in_len < seglen ? in_len : seglen;
+
+		/* There should be at least 1 additional block in out buffer */
+		if (inseglen > block_len) {
+			unsigned part = inseglen - block_len;
+			EVP_EncryptUpdate(ctx, insegaddr, &cipher_len, insegaddr, part);
+			IN_CONSUME(part);
+			out_pos += cipher_len;
+		}
+
+		/* Use temporal storage */
+		if (inseglen > 0) {
+			unsigned part = inseglen;
+			EVP_EncryptUpdate(ctx, block, &cipher_len, insegaddr, part);
+			IN_CONSUME(part);
+			odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+			out_pos += cipher_len;
+		}
+	}
+
+	ret = EVP_EncryptFinal_ex(ctx, block, &cipher_len);
+	odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+
+	return ret;
+}
+
+static
+int internal_decrypt(EVP_CIPHER_CTX *ctx, odp_crypto_op_param_t *param)
+{
+	odp_packet_t pkt = param->out_pkt;
+	unsigned in_pos = param->cipher_range.offset;
+	unsigned out_pos = param->cipher_range.offset;
+	unsigned in_len = param->cipher_range.length;
+	uint8_t block[2 * EVP_MAX_BLOCK_LENGTH];
+	unsigned block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
+	int cipher_len;
+	int ret;
+
+	while (in_len > 0) {
+		uint32_t seglen = 0; /* GCC */
+		uint8_t *insegaddr = odp_packet_offset(pkt, in_pos, &seglen, NULL);
+		unsigned inseglen = in_len < seglen ? in_len : seglen;
+
+		/* There should be at least 1 additional block in out buffer */
+		if (inseglen > block_len) {
+			unsigned part = inseglen - block_len;
+			EVP_DecryptUpdate(ctx, insegaddr, &cipher_len, insegaddr, part);
+			IN_CONSUME(part);
+			out_pos += cipher_len;
+		}
+
+		/* Use temporal storage */
+		if (inseglen > 0) {
+			unsigned part = inseglen;
+			EVP_DecryptUpdate(ctx, block, &cipher_len, insegaddr, part);
+			IN_CONSUME(part);
+			odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+			out_pos += cipher_len;
+		}
+	}
+
+	ret = EVP_DecryptFinal_ex(ctx, block, &cipher_len);
+	odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+
+	return ret;
+}
+
 static
 odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
 				    odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t plain_len   = param->cipher_range.length;
 	void *iv_ptr;
-	int cipher_len = 0;
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -212,9 +298,6 @@ odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Encrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -222,13 +305,12 @@ odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
 	EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
 
-	EVP_EncryptUpdate(ctx, data, &cipher_len, data, plain_len);
-
-	EVP_EncryptFinal_ex(ctx, data + cipher_len, &cipher_len);
+	ret = internal_encrypt(ctx, param);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static
@@ -236,10 +318,8 @@ odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
 				    odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t cipher_len   = param->cipher_range.length;
-	int plain_len = 0;
 	void *iv_ptr;
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -248,9 +328,6 @@ odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Decrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -258,13 +335,12 @@ odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
 	EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
 
-	EVP_DecryptUpdate(ctx, data, &plain_len, data, cipher_len);
-
-	EVP_DecryptFinal_ex(ctx, data + plain_len, &plain_len);
+	ret = internal_decrypt(ctx, param);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static int process_cipher_param(odp_crypto_generic_session_t *session,
@@ -299,13 +375,12 @@ odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
 				     odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t plain_len   = param->cipher_range.length;
 	const uint8_t *aad_head = param->aad.ptr;
 	uint32_t aad_len = param->aad.length;
 	void *iv_ptr;
-	int cipher_len = 0;
-	uint8_t *tag = data + param->hash_result_offset;
+	int dummy_len = 0;
+	uint8_t block[EVP_MAX_MD_SIZE];
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -314,9 +389,6 @@ odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Encrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -328,18 +400,20 @@ odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
 
 	/* Authenticate header data (if any) without encrypting them */
 	if (aad_len > 0)
-		EVP_EncryptUpdate(ctx, NULL, &cipher_len,
+		EVP_EncryptUpdate(ctx, NULL, &dummy_len,
 				  aad_head, aad_len);
 
-	EVP_EncryptUpdate(ctx, data, &cipher_len, data, plain_len);
+	ret = internal_encrypt(ctx, param);
 
-	EVP_EncryptFinal_ex(ctx, data + cipher_len, &cipher_len);
 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
-			    session->p.auth_digest_len, tag);
+			    session->p.auth_digest_len, block);
+	odp_packet_copy_from_mem(param->out_pkt, param->hash_result_offset,
+			       session->p.auth_digest_len, block);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static
@@ -347,13 +421,12 @@ odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
 				     odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t cipher_len   = param->cipher_range.length;
 	const uint8_t *aad_head = param->aad.ptr;
 	uint32_t aad_len = param->aad.length;
-	int plain_len = 0;
+	int dummy_len = 0;
 	void *iv_ptr;
-	uint8_t *tag   = data + param->hash_result_offset;
+	uint8_t block[EVP_MAX_MD_SIZE];
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -362,9 +435,6 @@ odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Decrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -374,22 +444,22 @@ odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
 	EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
 
+	odp_packet_copy_to_mem(param->out_pkt, param->hash_result_offset,
+			       session->p.auth_digest_len, block);
 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
-			    session->p.auth_digest_len, tag);
+			    session->p.auth_digest_len, block);
 
 	/* Authenticate header data (if any) without encrypting them */
 	if (aad_len > 0)
-		EVP_DecryptUpdate(ctx, NULL, &plain_len,
+		EVP_DecryptUpdate(ctx, NULL, &dummy_len,
 				  aad_head, aad_len);
 
-	EVP_DecryptUpdate(ctx, data, &plain_len, data, cipher_len);
-
-	if (EVP_DecryptFinal_ex(ctx, data + plain_len, &plain_len) <= 0)
-		return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+	ret = internal_decrypt(ctx, param);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK:
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static int process_aes_gcm_param(odp_crypto_generic_session_t *session,
