aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/doc/src/notes.xml52
-rw-r--r--erts/emulator/beam/atom.c77
-rw-r--r--erts/emulator/beam/atom.h8
-rw-r--r--erts/emulator/beam/beam_emu.c20
-rw-r--r--erts/emulator/beam/erl_bif_info.c2
-rw-r--r--erts/emulator/beam/erl_bif_trace.c29
-rw-r--r--erts/emulator/beam/erl_process.h5
-rw-r--r--erts/emulator/beam/instrs.tab17
-rw-r--r--erts/emulator/drivers/common/inet_drv.c190
-rw-r--r--erts/emulator/test/bif_SUITE.erl10
-rw-r--r--erts/emulator/test/exception_SUITE.erl34
-rw-r--r--erts/emulator/test/process_SUITE.erl84
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin82620 -> 83096 bytes
-rw-r--r--erts/preloaded/src/prim_inet.erl97
-rw-r--r--erts/vsn.mk2
15 files changed, 495 insertions, 132 deletions
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index e8a7d88f9e..3ff3bfc726 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,58 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.3.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>In nested use of <c>try</c>/<c>catch</c>, rethrowing
+ an exception using <c>erlang:raise/3</c> with a different
+ class would not always be able to change the class of the
+ exception.</p>
+ <p>
+ Own Id: OTP-15834 Aux Id: ERIERL-367 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>seq_trace:set_token(label,Term)</c> which
+ could cause VM crash if <c>Term</c> was heap allocated
+ (not an atom, small integer, local pid or port). Bug
+ exists since OTP 21.0 when terms other than small
+ integers were first allowed as labels.</p>
+ <p>
+ Own Id: OTP-15849 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ Fix <c>seq_trace:print/2</c> not to raise <c>badarg</c>
+ exception if label is not a small integer. Bug exists
+ since OTP 21.0.</p>
+ <p>
+ Own Id: OTP-15859 Aux Id: ERL-700 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3.5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a buffer overflow when
+ <c>binary_to_existing_atom/2</c> and
+ <c>list_to_existing_atom/2</c> was used with the
+ <c>latin1</c> encoding.</p>
+ <p>
+ Own Id: OTP-15819 Aux Id: ERL-944 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.3.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index 59b51fd15e..5a70509ffd 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -200,11 +200,15 @@ atom_free(Atom* obj)
ASSERT(obj->slot.index == atom_val(am_ErtsSecretAtom));
}
-static void latin1_to_utf8(byte* conv_buf, const byte** srcp, int* lenp)
+static void latin1_to_utf8(byte* conv_buf, Uint buf_sz,
+ const byte** srcp, Uint* lenp)
{
byte* dst;
const byte* src = *srcp;
- int i, len = *lenp;
+ Uint i, len = *lenp;
+
+ ASSERT(len <= MAX_ATOM_CHARACTERS);
+ ASSERT(buf_sz >= MAX_ATOM_SZ_FROM_LATIN1);
for (i=0 ; i < len; ++i) {
if (src[i] & 0x80) {
@@ -234,11 +238,11 @@ need_convertion:
* erts_atom_put_index() may fail. Returns negative indexes for errors.
*/
int
-erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
+erts_atom_put_index(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc)
{
byte utf8_copy[MAX_ATOM_SZ_FROM_LATIN1];
const byte *text = name;
- int tlen = len;
+ Uint tlen;
Sint no_latin1_chars;
Atom a;
int aix;
@@ -247,13 +251,16 @@ erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
erts_atomic_inc_nob(&atom_put_ops);
#endif
- if (tlen < 0) {
- if (trunc)
- tlen = 0;
- else
- return ATOM_MAX_CHARS_ERROR;
+ if (len < 0) {
+ if (trunc) {
+ len = 0;
+ } else {
+ return ATOM_MAX_CHARS_ERROR;
+ }
}
+ tlen = len;
+
switch (enc) {
case ERTS_ATOM_ENC_7BIT_ASCII:
if (tlen > MAX_ATOM_CHARACTERS) {
@@ -277,7 +284,7 @@ erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
return ATOM_MAX_CHARS_ERROR;
}
no_latin1_chars = tlen;
- latin1_to_utf8(utf8_copy, &text, &tlen);
+ latin1_to_utf8(utf8_copy, sizeof(utf8_copy), &text, &tlen);
break;
case ERTS_ATOM_ENC_UTF8:
/* First sanity check; need to verify later */
@@ -338,7 +345,7 @@ erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
* erts_atom_put() may fail. If it fails THE_NON_VALUE is returned!
*/
Eterm
-erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
+erts_atom_put(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc)
{
int aix = erts_atom_put_index(name, len, enc, trunc);
if (aix >= 0)
@@ -348,7 +355,7 @@ erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc)
}
Eterm
-am_atom_put(const char* name, int len)
+am_atom_put(const char* name, Sint len)
{
/* Assumes 7-bit ascii; use erts_atom_put() for other encodings... */
return erts_atom_put((byte *) name, len, ERTS_ATOM_ENC_7BIT_ASCII, 1);
@@ -379,23 +386,57 @@ int atom_table_sz(void)
}
int
-erts_atom_get(const char *name, int len, Eterm* ap, ErtsAtomEncoding enc)
+erts_atom_get(const char *name, Uint len, Eterm* ap, ErtsAtomEncoding enc)
{
byte utf8_copy[MAX_ATOM_SZ_FROM_LATIN1];
Atom a;
int i;
int res;
- a.len = (Sint16) len;
- a.name = (byte *)name;
- if (enc == ERTS_ATOM_ENC_LATIN1) {
- latin1_to_utf8(utf8_copy, (const byte**)&a.name, &len);
- a.len = (Sint16) len;
+ switch (enc) {
+ case ERTS_ATOM_ENC_LATIN1:
+ if (len > MAX_ATOM_CHARACTERS) {
+ return 0;
+ }
+
+ latin1_to_utf8(utf8_copy, sizeof(utf8_copy), (const byte**)&name, &len);
+
+ a.name = (byte*)name;
+ a.len = (Sint16)len;
+ break;
+ case ERTS_ATOM_ENC_7BIT_ASCII:
+ if (len > MAX_ATOM_CHARACTERS) {
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (name[i] & 0x80) {
+ return 0;
+ }
+ }
+
+ a.len = (Sint16)len;
+ a.name = (byte*)name;
+ break;
+ case ERTS_ATOM_ENC_UTF8:
+ if (len > MAX_ATOM_SZ_LIMIT) {
+ return 0;
+ }
+
+ /* We don't need to check whether the encoding is legal as all atom
+ * names are stored as UTF-8 and we know a lookup with a badly encoded
+ * name will fail. */
+
+ a.len = (Sint16)len;
+ a.name = (byte*)name;
+ break;
}
+
atom_read_lock();
i = index_get(&erts_atom_table, (void*) &a);
res = i < 0 ? 0 : (*ap = make_atom(i), 1);
atom_read_unlock();
+
return res;
}
diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h
index ca920679c6..f51c5a8c62 100644
--- a/erts/emulator/beam/atom.h
+++ b/erts/emulator/beam/atom.h
@@ -133,14 +133,14 @@ typedef enum {
int atom_table_size(void); /* number of elements */
int atom_table_sz(void); /* table size in bytes, excluding stored objects */
-Eterm am_atom_put(const char*, int); /* ONLY 7-bit ascii! */
-Eterm erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc);
-int erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc);
+Eterm am_atom_put(const char*, Sint); /* ONLY 7-bit ascii! */
+Eterm erts_atom_put(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc);
+int erts_atom_put_index(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc);
void init_atom_table(void);
void atom_info(fmtfn_t, void *);
void dump_atoms(fmtfn_t, void *);
Uint erts_get_atom_limit(void);
-int erts_atom_get(const char* name, int len, Eterm* ap, ErtsAtomEncoding enc);
+int erts_atom_get(const char* name, Uint len, Eterm* ap, ErtsAtomEncoding enc);
void erts_atom_get_text_space_sizes(Uint *reserved, Uint *used);
#endif
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 3af0838794..ec4f9b4339 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -422,6 +422,7 @@ static Eterm add_stacktrace(Process* c_p, Eterm Value, Eterm exc);
static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
ErtsCodeMFA *bif_mfa, Eterm args);
static struct StackTrace * get_trace_from_exc(Eterm exc);
+static Eterm *get_freason_ptr_from_exc(Eterm exc);
static Eterm make_arglist(Process* c_p, Eterm* reg, int a);
void
@@ -1904,6 +1905,25 @@ static int is_raised_exc(Eterm exc) {
}
}
+static Eterm *get_freason_ptr_from_exc(Eterm exc) {
+ static Eterm dummy_freason;
+ struct StackTrace* s;
+
+ if (exc == NIL) {
+ /*
+ * Is is not exactly clear when exc can be NIL. Probably only
+ * when the exception has been generated from native code.
+ * Return a pointer to an Eterm that can be safely written and
+ * ignored.
+ */
+ return &dummy_freason;
+ } else {
+ ASSERT(is_list(exc));
+ s = (struct StackTrace *) big_val(CDR(list_val(exc)));
+ return &s->freason;
+ }
+}
+
/*
* Creating a list with the argument registers
*/
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 96f399fbbe..16c06766fb 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -761,7 +761,7 @@ static ErtsProcessInfoArgs pi_args[] = {
{am_memory, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
{am_garbage_collection, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + ERTS_MAX_HEAP_SIZE_MAP_SZ, 0, ERTS_PROC_LOCK_MAIN},
{am_group_leader, 0, 0, ERTS_PROC_LOCK_MAIN},
- {am_reductions, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
+ {am_reductions, 0, 0, ERTS_PROC_LOCK_MAIN},
{am_priority, 0, 0, 0},
{am_trace, 0, 0, ERTS_PROC_LOCK_MAIN},
{am_binary, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN},
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 711e62c795..b31d5b86cb 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -74,7 +74,7 @@ static void smp_bp_finisher(void* arg);
static BIF_RETTYPE
system_monitor(Process *p, Eterm monitor_pid, Eterm list);
-static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/
+static void new_seq_trace_token(Process* p, int); /* help func for seq_trace_2*/
static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
@@ -1874,7 +1874,7 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2,
if (current_flag && ( (arg2 == am_true) || (arg2 == am_false)) ) {
/* Flags */
- new_seq_trace_token(p);
+ new_seq_trace_token(p, 0);
flags = unsigned_val(SEQ_TRACE_TOKEN_FLAGS(p));
if (build_result) {
old_value = flags & current_flag ? am_true : am_false;
@@ -1889,11 +1889,11 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2,
return old_value;
}
else if (arg1 == am_label) {
- new_seq_trace_token(p);
+ new_seq_trace_token(p, is_not_immed(arg2));
if (build_result) {
old_value = SEQ_TRACE_TOKEN_LABEL(p);
}
- SEQ_TRACE_TOKEN_LABEL(p) = arg2;
+ SEQ_TRACE_TOKEN_LABEL(p) = arg2;
return old_value;
}
else if (arg1 == am_serial) {
@@ -1905,7 +1905,7 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2,
if ((*tp != make_arityval(2)) || is_not_small(*(tp+1)) || is_not_small(*(tp+2))) {
return THE_NON_VALUE;
}
- new_seq_trace_token(p);
+ new_seq_trace_token(p, 0);
if (build_result) {
hp = HAlloc(p,3);
old_value = TUPLE2(hp, SEQ_TRACE_TOKEN_LASTCNT(p),
@@ -1940,8 +1940,8 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2,
}
}
-void
-new_seq_trace_token(Process* p)
+static void
+new_seq_trace_token(Process* p, int ensure_new_heap)
{
Eterm* hp;
@@ -1953,6 +1953,16 @@ new_seq_trace_token(Process* p)
p->common.id, /* Internal pid */ /* From */
make_small(p->seq_trace_lastcnt));
}
+ else if (ensure_new_heap) {
+ Eterm* tpl = tuple_val(SEQ_TRACE_TOKEN(p));
+ ASSERT(arityval(tpl[0]) == 5);
+ if (ErtsInArea(tpl, OLD_HEAP(p),
+ (OLD_HEND(p) - OLD_HEAP(p))*sizeof(Eterm))) {
+ hp = HAlloc(p, 6);
+ sys_memcpy(hp, tpl, 6*sizeof(Eterm));
+ SEQ_TRACE_TOKEN(p) = make_tuple(hp);
+ }
+ }
}
BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item)
@@ -2050,10 +2060,7 @@ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2)
if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) {
BIF_RET(am_false);
}
- if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) {
- BIF_ERROR(BIF_P, BADARG);
- }
- if (SEQ_TRACE_TOKEN_LABEL(BIF_P) != BIF_ARG_1)
+ if (!EQ(BIF_ARG_1, SEQ_TRACE_TOKEN_LABEL(BIF_P)))
BIF_RET(am_false);
seq_trace_update_send(BIF_P);
seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_2,
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 43937f216c..86798df64e 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1226,9 +1226,10 @@ void erts_check_for_holes(Process* p);
/* The sequential tracing token is a tuple of size 5:
*
- * {Flags, Label, Serial, Sender}
+ * {Flags, Label, Serial, Sender, LastCnt}
+ *
+ * WARNING: The top 5-tuple is *MUTABLE* and thus INTERNAL ONLY.
*/
-
#define SEQ_TRACE_TOKEN_ARITY(p) (arityval(*(tuple_val(SEQ_TRACE_TOKEN(p)))))
#define SEQ_TRACE_TOKEN_FLAGS(p) (*(tuple_val(SEQ_TRACE_TOKEN(p)) + 1))
#define SEQ_TRACE_TOKEN_LABEL(p) (*(tuple_val(SEQ_TRACE_TOKEN(p)) + 2))
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index 42c1168f85..999e9337ff 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -951,19 +951,30 @@ raw_raise() {
Eterm class = x(0);
Eterm value = x(1);
Eterm stacktrace = x(2);
+ Eterm* freason_ptr;
+
+ /*
+ * Note that the i_raise instruction will override c_p->freason
+ * with the freason field stored inside the StackTrace struct in
+ * ftrace. Therefore, we must take care to store the class both
+ * inside the StackTrace struct and in c_p->freason (important if
+ * the class is different from the class of the original
+ * exception).
+ */
+ freason_ptr = get_freason_ptr_from_exc(stacktrace);
if (class == am_error) {
- c_p->freason = EXC_ERROR & ~EXF_SAVETRACE;
+ *freason_ptr = c_p->freason = EXC_ERROR & ~EXF_SAVETRACE;
c_p->fvalue = value;
c_p->ftrace = stacktrace;
goto find_func_info;
} else if (class == am_exit) {
- c_p->freason = EXC_EXIT & ~EXF_SAVETRACE;
+ *freason_ptr = c_p->freason = EXC_EXIT & ~EXF_SAVETRACE;
c_p->fvalue = value;
c_p->ftrace = stacktrace;
goto find_func_info;
} else if (class == am_throw) {
- c_p->freason = EXC_THROWN & ~EXF_SAVETRACE;
+ *freason_ptr = c_p->freason = EXC_THROWN & ~EXF_SAVETRACE;
c_p->fvalue = value;
c_p->ftrace = stacktrace;
goto find_func_info;
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index b71ce0389d..d6ceab680b 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2018. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2019. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -3379,6 +3379,71 @@ static int udp_parse_ancillary_data(ErlDrvTermData *spec, int i,
i = LOAD_NIL(spec, i);
return LOAD_LIST(spec, i, n+1);
}
+
+static int compile_ancillary_data(struct msghdr *mhdr,
+ char *ptr, ErlDrvSizeT anc_len) {
+ struct cmsghdr *cmsg;
+ size_t controllen = 0;
+ cmsg = CMSG_FIRSTHDR(mhdr);
+ for (;;) {
+ if (anc_len == 0) {
+ /* End of options to compile */
+ mhdr->msg_controllen = controllen;
+ return 0;
+ }
+ if (cmsg == NULL) {
+ /* End of destination before end of options */
+ return 1;
+ }
+
+#define COMPILE_ANCILLARY_DATA_ITEM(Level, Opt, Type, Get, Size) \
+ do { \
+ if (anc_len < (Size)) return 1; \
+ sys_memset(cmsg, '\0', CMSG_SPACE(sizeof(Type))); \
+ cmsg->cmsg_level = Level; \
+ cmsg->cmsg_type = Opt; \
+ cmsg->cmsg_len = CMSG_LEN(sizeof(Type)); \
+ *((Type *) CMSG_DATA(cmsg)) = Get(ptr); \
+ controllen += CMSG_SPACE(sizeof(Type)); \
+ cmsg = CMSG_NXTHDR(mhdr, cmsg); \
+ ptr += 4; \
+ anc_len -= 4; \
+ } while (0)
+#define SIZEOF_ANCILLARY_DATA (2 * CMSG_SPACE(sizeof(int)))
+ /* (IP_TOS | IPV6_TCLASS) + IP_TTL */
+
+ switch (anc_len--, *ptr++) {
+ case INET_OPT_TOS: {
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ COMPILE_ANCILLARY_DATA_ITEM(IPPROTO_IP, IP_TOS, int, get_int32, 4);
+#else
+ return 1; /* Socket option not implemented */
+#endif
+ break;
+ }
+ case INET_OPT_TTL: {
+#if defined(IPPROTO_IP) && defined(IP_TTL)
+ COMPILE_ANCILLARY_DATA_ITEM(IPPROTO_IP, IP_TTL, int, get_int32, 4);
+#else
+ return 1; /* Socket option not implemented */
+#endif
+ break;
+ }
+ case INET_OPT_TCLASS: {
+#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)
+ COMPILE_ANCILLARY_DATA_ITEM(IPPROTO_IPV6, IPV6_TCLASS, int, get_int32, 4);
+#else
+ return 1; /* Socket option not implemented */
+#endif
+ break;
+ }
+ default:
+ /* Unknow socket option */
+ return 1;
+ }
+#undef COMPILE_ANCILLARY_DATA_ITEM
+ }
+}
#endif /* ifndef __WIN32__ */
/*
@@ -9846,10 +9911,8 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s,
copy_desc->send_timeout = desc->send_timeout;
copy_desc->send_timeout_close = desc->send_timeout_close;
- if (desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET)
- copy_desc->tcp_add_flags |= TCP_ADDF_SHOW_ECONNRESET;
- else
- copy_desc->tcp_add_flags &= ~TCP_ADDF_SHOW_ECONNRESET;
+ copy_desc->tcp_add_flags = desc->tcp_add_flags
+ & (TCP_ADDF_SHOW_ECONNRESET | TCP_ADDF_LINGER_ZERO);
/* The new port will be linked and connected to the original caller */
port = driver_create_port(port, owner, "tcp_inet", (ErlDrvData) copy_desc);
@@ -11386,7 +11449,7 @@ static int tcp_shutdown_error(tcp_descriptor* desc, int err)
static void tcp_inet_delay_send(ErlDrvData data, ErlDrvTermData dummy)
{
tcp_descriptor *desc = (tcp_descriptor*)data;
- (void)tcp_inet_output(desc, INETP(desc)->s);
+ (void)tcp_inet_output(desc, (HANDLE) INETP(desc)->s);
}
/*
@@ -12553,7 +12616,7 @@ static void packet_inet_timeout(ErlDrvData e)
sock_select(desc, FD_READ, 0);
async_error_am (desc, am_timeout);
} else {
- (void)packet_inet_input(udesc, desc->s);
+ (void)packet_inet_input(udesc, (HANDLE) desc->s);
}
}
@@ -12597,11 +12660,8 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
char ancd[CMSG_SPACE(sizeof(*sri))];
} cmsg;
- if (len < SCTP_GET_SENDPARAMS_LEN) {
- inet_reply_error(desc, EINVAL);
- return;
- }
-
+ if (len < SCTP_GET_SENDPARAMS_LEN) goto return_einval;
+
/* The ancillary data */
sri = (struct sctp_sndrcvinfo *) (CMSG_DATA(&cmsg.hdr));
/* Get the "sndrcvinfo" from the buffer, advancing the "ptr": */
@@ -12634,28 +12694,85 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
goto check_result_code;
}
#endif
- /* UDP socket. Even if it is connected, there is an address prefix
- here -- ignored for connected sockets: */
- sz = len;
- qtr = ptr;
- xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz);
- if (xerror != NULL) {
- inet_reply_error_am(desc, driver_mk_atom(xerror));
- return;
- }
- len -= (qtr - ptr);
- ptr = qtr;
- /* Now "ptr" is the user data ptr, "len" is data length: */
- inet_output_count(desc, len);
-
- if (desc->state & INET_F_ACTIVE) { /* connected (ignore address) */
- code = sock_send(desc->s, ptr, len, 0);
- }
- else {
- code = sock_sendto(desc->s, ptr, len, 0, &other.sa, sz);
+ {
+ ErlDrvSizeT anc_len;
+
+ /* UDP socket. Even if it is connected, there is an address prefix
+ here -- ignored for connected sockets: */
+ sz = len;
+ qtr = ptr;
+ xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz);
+ if (xerror != NULL) {
+ inet_reply_error_am(desc, driver_mk_atom(xerror));
+ return;
+ }
+ len -= (qtr - ptr);
+ ptr = qtr;
+
+ /* Here comes ancillary data */
+ if (len < 4) goto return_einval;
+ anc_len = get_int32(ptr);
+ len -= 4; ptr += 4;
+ if (len < anc_len) goto return_einval;
+
+ if (anc_len == 0 && !!0/*XXX-short-circuit-for-testing*/) {
+ /* Empty ancillary data */
+ /* Now "ptr" is the user data ptr, "len" is data length: */
+ inet_output_count(desc, len);
+ if (desc->state & INET_F_ACTIVE) {
+ /* connected (ignore address) */
+ code = sock_send(desc->s, ptr, len, 0);
+ }
+ else {
+ code = sock_sendto(desc->s, ptr, len, 0, &other.sa, sz);
+ }
+ }
+ else {
+#ifdef __WIN32__
+ goto return_einval; /* Can not send ancillary data on Windows */
+#else
+ struct iovec iov[1];
+ struct msghdr mhdr;
+ union { /* For ancillary data */
+ struct cmsghdr hdr;
+ char ancd[SIZEOF_ANCILLARY_DATA];
+ } cmsg;
+ sys_memset(&iov, '\0', sizeof(iov));
+ sys_memset(&mhdr, '\0', sizeof(mhdr));
+ sys_memset(&cmsg, '\0', sizeof(cmsg));
+ if (desc->state & INET_F_ACTIVE) {
+ /* connected (ignore address) */
+ mhdr.msg_name = NULL;
+ mhdr.msg_namelen = 0;
+ }
+ else {
+ mhdr.msg_name = &other;
+ mhdr.msg_namelen = sz;
+ }
+ mhdr.msg_control = cmsg.ancd;
+ mhdr.msg_controllen = sizeof(cmsg.ancd);
+ if (compile_ancillary_data(&mhdr, ptr, anc_len) != 0) {
+ goto return_einval;
+ }
+ if (mhdr.msg_controllen == 0) {
+ /* XXX Testing - only possible for anc_len == 0 */
+ mhdr.msg_control = NULL;
+ }
+ len -= anc_len;
+ ptr += anc_len;
+ /* Now "ptr" is the user data ptr, "len" is data length: */
+ iov[0].iov_len = len;
+ iov[0].iov_base = ptr;
+ mhdr.msg_iov = iov;
+ mhdr.msg_iovlen = 1;
+ mhdr.msg_flags = 0;
+ inet_output_count(desc, len);
+ code = sock_sendmsg(desc->s, &mhdr, 0);
+#endif
+ }
}
-#ifdef HAVE_SCTP
+#ifdef HAVE_SCTP
check_result_code:
/* "code" analysis is the same for both SCTP and UDP cases above: */
#endif
@@ -12665,8 +12782,15 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
}
else
inet_reply_ok(desc);
+ return;
+
+ return_einval:
+ inet_reply_error(desc, EINVAL);
+ return;
}
-#endif
+
+#endif /* HAVE_UDP */
+
#ifdef __WIN32__
static void packet_inet_event(ErlDrvData e, ErlDrvEvent event)
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 9e7bcd5255..9f6e1059b9 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -609,6 +609,16 @@ binary_to_existing_atom(Config) when is_list(Config) ->
UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1),
UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1),
+
+ %% ERL-944; a binary that was too large would overflow the latin1-to-utf8
+ %% conversion buffer.
+ OverflowAtom = <<0:511/unit:8,
+ 196, 133, 196, 133, 196, 133, 196, 133, 196, 133,
+ 196, 133, 196, 133, 196, 133, 196, 133, 196, 133,
+ 196, 133, 196, 133, 196, 133, 196, 133, 196, 133,
+ 196, 133, 196, 133, 196, 133, 196, 133, 196, 133>>,
+ {'EXIT', _} = (catch binary_to_existing_atom(OverflowAtom, latin1)),
+
ok.
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
index aec66cb9a3..6e6f7d78ab 100644
--- a/erts/emulator/test/exception_SUITE.erl
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -23,6 +23,7 @@
-export([all/0, suite/0,
badmatch/1, pending_errors/1, nil_arith/1, top_of_stacktrace/1,
stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1,
+ change_exception_class/1,
exception_with_heap_frag/1, backtrace_depth/1,
line_numbers/1]).
@@ -43,6 +44,7 @@ suite() ->
all() ->
[badmatch, pending_errors, nil_arith, top_of_stacktrace,
stacktrace, nested_stacktrace, raise, gunilla, per,
+ change_exception_class,
exception_with_heap_frag, backtrace_depth, line_numbers].
-define(try_match(E),
@@ -507,6 +509,38 @@ t1(_,X,_) ->
t2(_,X,_) ->
(X bsl 1) + 1.
+change_exception_class(_Config) ->
+ try
+ change_exception_class_1(fun() -> throw(arne) end)
+ catch
+ error:arne ->
+ ok;
+ Class:arne ->
+ ct:fail({wrong_exception_class,Class})
+ end.
+
+change_exception_class_1(F) ->
+ try
+ change_exception_class_2(F)
+ after
+ %% The exception would be caught and rethrown using
+ %% an i_raise instruction. Before the correction
+ %% of the raw_raise instruction, the change of class
+ %% would not stick.
+ io:put_chars("Exception automatically rethrown here\n")
+ end.
+
+change_exception_class_2(F) ->
+ try
+ F()
+ catch
+ throw:Reason:Stack ->
+ %% Translated to a raw_raise instruction.
+ %% The change of exception class would not stick
+ %% if the i_raise instruction was later executed.
+ erlang:raise(error, Reason, Stack)
+ end.
+
%%
%% Make sure that even if a BIF builds an heap fragment, then causes an exception,
%% the stacktrace term will still be OK (specifically, that it does not contain
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 0cb0d6c1e2..cef1b79035 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -1089,42 +1089,86 @@ process_info_status_handled_signal(Config) when is_list(Config) ->
%% OTP-15709
%% Provoke a bug where process_info(reductions) returned wrong result
%% because REDS_IN (def_arg_reg[5]) is read when the process in not running.
+%%
+%% And a bug where process_info(reductions) on a process which was releasing its
+%% main lock during execution could result in negative reduction diffs.
process_info_reductions(Config) when is_list(Config) ->
- pi_reductions_tester(spawn_link(fun() -> pi_reductions_spinnloop() end)),
- pi_reductions_tester(spawn_link(fun() -> pi_reductions_recvloop() end)),
+ {S1, S2} = case erlang:system_info(schedulers) of
+ 1 -> {1,1};
+ _ -> {1,2}
+ end,
+ io:format("Run on schedulers ~p and ~p\n", [S1,S2]),
+ Boss = self(),
+ Doer = spawn_opt(fun () ->
+ pi_reductions_tester(true, 10, fun pi_reductions_spinnloop/0, S2),
+ pi_reductions_tester(true, 10, fun pi_reductions_recvloop/0, S2),
+ pi_reductions_tester(false, 100, fun pi_reductions_main_unlocker/0, S2),
+ Boss ! {self(), done}
+ end,
+ [link, {scheduler, S1}]),
+
+ {Doer, done} = receive M -> M end,
ok.
-pi_reductions_tester(Pid) ->
- {_, DiffList} =
- lists:foldl(fun(_, {Prev, Acc}) ->
- %% Add another item that force sending the request
- %% as a signal, like 'current_function'.
- PI = process_info(Pid, [reductions, current_function]),
- [{reductions,Reds}, {current_function,_}] = PI,
- Diff = Reds - Prev,
- {Diff, true} = {Diff, (Diff >= 0)},
- {Diff, true} = {Diff, (Diff =< 1000*1000)},
- {Reds, [Diff | Acc]}
- end,
- {0, []},
- lists:seq(1,10)),
+pi_reductions_tester(ForceSignal, MaxCalls, Fun, S2) ->
+ Pid = spawn_opt(Fun, [link, {scheduler,S2}]),
+ Extra = case ForceSignal of
+ true ->
+ %% Add another item that force sending the request
+ %% as a signal, like 'current_function'.
+ [current_function];
+ false ->
+ []
+ end,
+ LoopFun = fun Me(Calls, Prev, Acc0) ->
+ PI = process_info(Pid, [reductions | Extra]),
+ [{reductions,Reds} | _] = PI,
+ Diff = Reds - Prev,
+ %% Verify we get sane non-negative reduction diffs
+ {Diff, true} = {Diff, (Diff >= 0)},
+ {Diff, true} = {Diff, (Diff =< 1000*1000)},
+ Acc1 = [Diff | Acc0],
+ case Calls >= MaxCalls of
+ true -> Acc1;
+ false -> Me(Calls+1, Reds, Acc1)
+ end
+ end,
+ DiffList = LoopFun(0, 0, []),
unlink(Pid),
exit(Pid,kill),
- io:format("Reduction diffs: ~p\n", [DiffList]),
+ io:format("Reduction diffs: ~p\n", [lists:reverse(DiffList)]),
ok.
pi_reductions_spinnloop() ->
%% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN
- pi_reductions_spinnloop(1, atom, "hej", self(), make_ref(), 3.14).
+ pi_reductions_spinnloop(999*1000, atom, "hej", self(), make_ref(), 3.14).
-pi_reductions_spinnloop(A,B,C,D,E,F) ->
- pi_reductions_spinnloop(B,C,D,E,F,A).
+pi_reductions_spinnloop(N,A,B,C,D,E) when N > 0 ->
+ pi_reductions_spinnloop(N-1,B,C,D,E,A);
+pi_reductions_spinnloop(0,_,_,_,_,_) ->
+ %% Stop to limit max number of reductions consumed
+ pi_reductions_recvloop().
pi_reductions_recvloop() ->
receive
"a free lunch" -> false
end.
+pi_reductions_main_unlocker() ->
+ Other = spawn_link(fun() -> receive die -> ok end end),
+ pi_reductions_main_unlocker_loop(Other).
+
+pi_reductions_main_unlocker_loop(Other) ->
+ %% Assumption: register(OtherPid, Name) will unlock main lock of calling
+ %% process during execution.
+ register(pi_reductions_main_unlocker, Other),
+ unregister(pi_reductions_main_unlocker),
+
+ %% Yield in order to increase probability of process_info sometimes probing
+ %% this process when it's not RUNNING.
+ erlang:yield(),
+ pi_reductions_main_unlocker_loop(Other).
+
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index d3614d5f16..8833f9c77c 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 2820a5bef4..77d4292ad0 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -553,34 +553,49 @@ send(S, Data) ->
%% "sendto" is for UDP. IP and Port are set by the caller to 0 if the socket
%% is known to be connected.
-sendto(S, Addr, _, Data) when is_port(S), tuple_size(Addr) =:= 2 ->
- case type_value(set, addr, Addr) of
- true ->
- ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p)~n", [S,Addr,Data]),
- try
- erlang:port_command(S, [enc_value(set, addr, Addr),Data])
- of
- true ->
- receive
- {inet_reply,S,Reply} ->
- ?DBG_FORMAT(
- "prim_inet:sendto() -> ~p~n", [Reply]),
- Reply
- end
- catch
- error:_ ->
- ?DBG_FORMAT(
- "prim_inet:sendto() -> {error,einval}~n", []),
- {error,einval}
- end;
- false ->
- ?DBG_FORMAT(
- "prim_inet:sendto() -> {error,einval}~n", []),
- {error,einval}
- end;
-sendto(S, IP, Port, Data) ->
- sendto(S, {IP, Port}, 0, Data).
-
+sendto(S, {_, _} = Address, AncOpts, Data)
+ when is_port(S), is_list(AncOpts) ->
+ case encode_opt_val(AncOpts) of
+ {ok, AncData} ->
+ AncDataLen = iolist_size(AncData),
+ case
+ type_value(set, addr, Address) andalso
+ type_value(set, uint32, AncDataLen)
+ of
+ true ->
+ ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p, ~p)~n",
+ [S,Address,AncOpts,Data]),
+ PortCommandData =
+ [enc_value(set, addr, Address),
+ enc_value(set, uint32, AncDataLen), AncData,
+ Data],
+ try erlang:port_command(S, PortCommandData) of
+ true ->
+ receive
+ {inet_reply,S,Reply} ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> ~p~n", [Reply]),
+ Reply
+ end
+ catch
+ _:_ ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+ false ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+ {error,_} ->
+ ?DBG_FORMAT(
+ "prim_inet:sendto() -> {error,einval}~n", []),
+ {error,einval}
+ end;
+sendto(S, IP, Port, Data)
+ when is_port(S), is_integer(Port) ->
+ sendto(S, {IP, Port}, [], Data).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -1993,15 +2008,15 @@ enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) ->
[?INET_AF_LOCAL,iolist_size(File)|File];
%%
enc_value_2(addr, {inet,{any,Port}}) ->
- [?INET_AF_INET,?int16(Port),0,0,0,0];
+ [?INET_AF_INET,?int16(Port)|ip4_to_bytes({0,0,0,0})];
enc_value_2(addr, {inet,{loopback,Port}}) ->
- [?INET_AF_INET,?int16(Port),127,0,0,1];
+ [?INET_AF_INET,?int16(Port)|ip4_to_bytes({127,0,0,1})];
enc_value_2(addr, {inet,{IP,Port}}) ->
[?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
enc_value_2(addr, {inet6,{any,Port}}) ->
- [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,0];
+ [?INET_AF_INET6,?int16(Port)|ip6_to_bytes({0,0,0,0,0,0,0,0})];
enc_value_2(addr, {inet6,{loopback,Port}}) ->
- [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,1];
+ [?INET_AF_INET6,?int16(Port)|ip6_to_bytes({0,0,0,0,0,0,0,1})];
enc_value_2(addr, {inet6,{IP,Port}}) ->
[?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
enc_value_2(addr, {local,Addr}) ->
@@ -2149,10 +2164,10 @@ enum_name(_, []) -> false.
%% encode opt/val REVERSED since options are stored in reverse order
%% i.e. the recent options first (we must process old -> new)
encode_opt_val(Opts) ->
- try
- enc_opt_val(Opts, [])
+ try
+ {ok, enc_opt_val(Opts, [])}
catch
- Reason -> {error,Reason}
+ throw:Reason -> {error,Reason}
end.
%% {active, once} and {active, N} are specially optimized because they will
@@ -2171,17 +2186,21 @@ enc_opt_val([binary|Opts], Acc) ->
enc_opt_val(Opts, Acc, mode, binary);
enc_opt_val([list|Opts], Acc) ->
enc_opt_val(Opts, Acc, mode, list);
-enc_opt_val([_|_], _) -> {error,einval};
-enc_opt_val([], Acc) -> {ok,Acc}.
+enc_opt_val([_|_], _) ->
+ throw(einval);
+enc_opt_val([], Acc) ->
+ Acc.
enc_opt_val(Opts, Acc, Opt, Val) when is_atom(Opt) ->
Type = type_opt(set, Opt),
case type_value(set, Type, Val) of
true ->
enc_opt_val(Opts, [enc_opt(Opt),enc_value(set, Type, Val)|Acc]);
- false -> {error,einval}
+ false ->
+ throw(einval)
end;
-enc_opt_val(_, _, _, _) -> {error,einval}.
+enc_opt_val(_, _, _, _) ->
+ throw(einval).
diff --git a/erts/vsn.mk b/erts/vsn.mk
index c57f8746f6..b3fd73bab0 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.3.5
+VSN = 10.3.5.2
# Port number 4365 in 4.2
# Port number 4366 in 4.3